From 12100c506eba5feeaf5dc0f4ce33f3421e0dee5b Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 22 Jan 2024 11:41:14 -0800 Subject: [PATCH 01/21] Add support for custom CommandNotFound OSC --- .../Resources/en-US/Resources.resw | 14 ++- src/cascadia/TerminalApp/TerminalPage.cpp | 109 ++++++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 3 + src/cascadia/TerminalControl/ControlCore.cpp | 8 ++ src/cascadia/TerminalControl/ControlCore.h | 2 + src/cascadia/TerminalControl/ControlCore.idl | 1 + src/cascadia/TerminalControl/EventArgs.cpp | 1 + src/cascadia/TerminalControl/EventArgs.h | 10 ++ src/cascadia/TerminalControl/EventArgs.idl | 5 + src/cascadia/TerminalControl/TermControl.cpp | 1 + src/cascadia/TerminalControl/TermControl.h | 18 +-- src/cascadia/TerminalControl/TermControl.idl | 1 + src/cascadia/TerminalCore/Terminal.cpp | 5 + src/cascadia/TerminalCore/Terminal.hpp | 4 + src/cascadia/TerminalCore/TerminalApi.cpp | 8 ++ src/host/outputStream.cpp | 4 + src/host/outputStream.hpp | 2 + src/terminal/adapter/ITermDispatch.hpp | 2 + src/terminal/adapter/ITerminalApi.hpp | 2 + src/terminal/adapter/adaptDispatch.cpp | 48 ++++++++ src/terminal/adapter/adaptDispatch.hpp | 2 + src/terminal/adapter/termDispatch.hpp | 2 + .../parser/OutputStateMachineEngine.cpp | 5 + .../parser/OutputStateMachineEngine.hpp | 1 + 24 files changed, 249 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 3f91408815f..ccdda95446a 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -898,4 +898,16 @@ Restart the active pane connection - + + Windows Package Manager — WinGet + + + Try installing this package using winget: + + + Additional results can be found using the following command: + + + Insert Suggestion + + \ No newline at end of file diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 605110a0ad7..0924ae60df1 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1670,6 +1670,8 @@ namespace winrt::TerminalApp::implementation term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler }); + term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); + // Don't even register for the event if the feature is compiled off. if constexpr (Feature_ShellCompletions::IsEnabled()) { @@ -2952,6 +2954,113 @@ namespace winrt::TerminalApp::implementation ShowWindowChanged.raise(*this, args); } + void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs /*args*/) + { + // 1. Search winget for missing command; retrieve packages + // TODO CARLOS + + // TEMPORARY SOLUTION + //std::vector pkgList{ L"Microsoft.PowerToys", L"Microsoft.WindowsTerminal" }; + std::vector pkgList{ L"Microsoft.PowerToys" }; + bool tooManySuggestions = true; + std::wstring searchOption = L"id"; + std::wstring searchQuery = L"Microsoft.PowerToys"; + // END TEMPORARY SOLUTION + + // 2. Display packages in UI + if (!pkgList.empty()) + { + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --{} {}", searchOption, pkg)); + } + + std::wstring footer = tooManySuggestions ? + fmt::format(L"winget search --{} {}", searchOption, searchQuery) : + L""; + + ShowCommandNotFoundInfoBar(suggestions, footer); + } + } + + winrt::fire_and_forget TerminalPage::ShowCommandNotFoundInfoBar(const std::vector suggestions, std::wstring footerCmd) + { + co_await wil::resume_foreground(Dispatcher()); + auto infoBar = FindName(L"CommandNotFoundInfoBar").try_as(); + + // Insert the message as custom content so we can make it selectable + RichTextBlock infoBarMsg; + Windows::UI::Xaml::Documents::Paragraph paragraph; + + // Append header + Windows::UI::Xaml::Documents::Run header; + header.Text(RS_(L"CommandNotFoundInfoBarHeader")); + paragraph.Inlines().Append(header); + + // Append each suggestion (and format the code blocks) + for (auto suggestion : suggestions) + { + Windows::UI::Xaml::Documents::LineBreak lineBreak; + paragraph.Inlines().Append(lineBreak); + + Windows::UI::Xaml::Documents::Run bulletPoint; + bulletPoint.Text(L"• "); + paragraph.Inlines().Append(bulletPoint); + + Windows::UI::Xaml::Documents::Run suggestionRun; + suggestionRun.Text(suggestion); + suggestionRun.FontFamily(Media::FontFamily{ L"Cascadia Code" }); + paragraph.Inlines().Append(suggestionRun); + } + + // Append the footer, if appropriate + if (!footerCmd.empty()) + { + Windows::UI::Xaml::Documents::LineBreak lineBreak; + paragraph.Inlines().Append(lineBreak); + + Windows::UI::Xaml::Documents::Run footerRun; + footerRun.Text(RS_(L"CommandNotFoundInfoBarFooter") + L" "); + paragraph.Inlines().Append(footerRun); + + Windows::UI::Xaml::Documents::Run footerCmdRun; + footerCmdRun.Text(footerCmd); + footerCmdRun.FontFamily(Media::FontFamily{ L"Cascadia Code" }); + paragraph.Inlines().Append(footerCmdRun); + } + + infoBarMsg.Blocks().Append(paragraph); + infoBarMsg.IsTextSelectionEnabled(true); + + // Set up the action button, if appropriate + // We have to use a stack panel here to force the button to be on the bottom + StackPanel infoBarContent; + infoBarContent.Children().Append(infoBarMsg); + if (suggestions.size() == 1) + { + Button infoBarBtn; + infoBarBtn.Content(box_value(RS_(L"CommandNotFoundInfoBarButtonCaption"))); + // upon clicking the action button, we inject the suggestion into the active terminal + infoBarBtn.Click([weakThis{ get_weak() }, infoBar, suggestion{ suggestions.at(0) }](auto&&, auto&&) { + if (auto page{ weakThis.get() }) + { + if (auto termControl = page->_GetActiveControl()) + { + termControl.SendInput(suggestion); + } + } + infoBar.IsOpen(false); + }); + + infoBarContent.Children().Append(infoBarBtn); + } + + infoBar.Content(infoBarContent); + infoBar.IsOpen(true); + } + // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 639165d1730..d809de20b16 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -146,6 +146,7 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning() const; winrt::hstring KeyboardServiceDisabledText(); + winrt::fire_and_forget ShowCommandNotFoundInfoBar(const std::vector suggestions, std::wstring footer); winrt::fire_and_forget IdentifyWindow(); winrt::fire_and_forget RenameFailed(); @@ -518,6 +519,8 @@ namespace winrt::TerminalApp::implementation void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); + void _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args); + winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 4027e914d8f..f0ffe9cb588 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -125,6 +125,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); }; _terminal->CompletionsChangedCallback(pfnCompletionsChanged); + auto pfnSearchMissingCommand = std::bind(&ControlCore::_terminalSearchMissingCommand, this, std::placeholders::_1); + _terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand); + // MSFT 33353327: Initialize the renderer in the ctor instead of Initialize(). // We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go. // If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach @@ -1582,6 +1585,11 @@ 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) + { + _SearchMissingCommandHandlers(*this, winrt::make(winrt::hstring{ missingCommand })); + } + bool ControlCore::HasSelection() const { const auto lock = _terminal->LockForReading(); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index d79df07d1bc..05321644f84 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -275,6 +275,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event UpdateSelectionMarkers; til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; + til::typed_event SearchMissingCommand; til::typed_event<> CloseTerminalRequested; til::typed_event<> RestartTerminalRequested; @@ -371,6 +372,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); winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 01223fb1a14..4c33722684b 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -185,6 +185,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; event Windows.Foundation.TypedEventHandler RestartTerminalRequested; + event Windows.Foundation.TypedEventHandler SearchMissingCommand; event Windows.Foundation.TypedEventHandler Attached; diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 93e147feaa8..858e2d0a81b 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -20,3 +20,4 @@ #include "KeySentEventArgs.g.cpp" #include "CharSentEventArgs.g.cpp" #include "StringSentEventArgs.g.cpp" +#include "SearchMissingCommandEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 1c135cdf4f6..8fc26fec6df 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -20,6 +20,7 @@ #include "KeySentEventArgs.g.h" #include "CharSentEventArgs.g.h" #include "StringSentEventArgs.g.h" +#include "SearchMissingCommandEventArgs.g.h" namespace winrt::Microsoft::Terminal::Control::implementation { @@ -251,6 +252,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(winrt::hstring, Text); }; + + struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT + { + public: + SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) : + _MissingCommand(missingCommand) {} + + WINRT_PROPERTY(winrt::hstring, MissingCommand); + }; } namespace winrt::Microsoft::Terminal::Control::factory_implementation diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 3caea5a0f38..b9943044100 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -121,4 +121,9 @@ namespace Microsoft.Terminal.Control { String Text { get; }; } + + runtimeclass SearchMissingCommandEventArgs + { + String MissingCommand { get; }; + } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index de8ec920d3e..fbe85967716 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -101,6 +101,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested }); _revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged }); _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); + _revokers.SearchMissingCommand= _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 2a8b28567ec..e961797b894 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -182,15 +182,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE // Those attach the handler to the core directly, and will explode if // the core ever gets detached & reattached to another window. - BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); - BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); - BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); - BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(CopyToClipboard, IInspectable, Control::CopyToClipboardEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); + BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(SearchMissingCommand, IInspectable, Control::SearchMissingCommandEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); @@ -404,6 +405,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested; Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; + Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index b105748ff64..183d1b9b75a 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -70,6 +70,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler KeySent; event Windows.Foundation.TypedEventHandler CharSent; event Windows.Foundation.TypedEventHandler StringSent; + event Windows.Foundation.TypedEventHandler SearchMissingCommand; Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 6eabcdab072..81bf73f8904 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1250,6 +1250,11 @@ void Microsoft::Terminal::Core::Terminal::CompletionsChangedCallback(std::functi _pfnCompletionsChanged.swap(pfn); } +void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::function pfn) noexcept +{ + _pfnSearchMissingCommand.swap(pfn); +} + Scheme Terminal::GetColorScheme() const { const auto& renderSettings = GetRenderSettings(); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 80c5e3cc905..8bc37fc96ad 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -163,6 +163,8 @@ class Microsoft::Terminal::Core::Terminal final : void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; + void SearchMissingCommand(const std::wstring_view command) override; + #pragma endregion void ClearMark(); @@ -237,6 +239,7 @@ class Microsoft::Terminal::Core::Terminal final : 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 BlinkCursor() noexcept; void SetCursorOn(const bool isOn) noexcept; @@ -345,6 +348,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnShowWindowChanged; std::function _pfnPlayMidiNote; std::function _pfnCompletionsChanged; + std::function _pfnSearchMissingCommand; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9a5ea2221ac..31bc2aafc96 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -471,6 +471,14 @@ void Terminal::InvokeCompletions(std::wstring_view menuJson, unsigned int replac } } +void Terminal::SearchMissingCommand(const std::wstring_view command) +{ + if (_pfnSearchMissingCommand) + { + _pfnSearchMissingCommand(command); + } +} + void Terminal::NotifyBufferRotation(const int delta) { // Update our selection, so it doesn't move as the buffer is cycled diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 70b270e3cd8..7924ff6b77a 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -447,3 +447,7 @@ void ConhostInternalGetSet::InvokeCompletions(std::wstring_view /*menuJson*/, un { // Not implemented for conhost. } +void ConhostInternalGetSet::SearchMissingCommand(std::wstring_view /*missingCommand*/) +{ + // Not implemented for conhost. +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 45ea6052e21..70e7ee940e3 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -76,6 +76,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) override; + void SearchMissingCommand(std::wstring_view missingCommand) override; + private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index d0c96b6949e..db1905af43a 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -141,6 +141,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool DoVsCodeAction(const std::wstring_view string) = 0; + virtual bool DoWTAction(const std::wstring_view string) = 0; + virtual StringHandler DownloadDRCS(const VTInt fontNumber, const VTParameter startChar, const DispatchTypes::DrcsEraseControl eraseControl, diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 3375a968b04..3f98c30c69b 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -87,5 +87,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void MarkCommandFinish(std::optional error) = 0; virtual void InvokeCompletions(std::wstring_view menuJson, unsigned int replaceLength) = 0; + + virtual void SearchMissingCommand(const std::wstring_view command) = 0; }; } diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index dfe540936d0..f6607ebb616 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -3890,6 +3890,54 @@ bool AdaptDispatch::DoVsCodeAction(const std::wstring_view string) return false; } +// Method Description: +// - Performs a Windows Terminal action +// - Currently, the actions we support are: +// * CmdNotFound: A protocol for passing commands that the shell couldn't resolve +// to the terminal. The command is then shared with WinGet to see if it can +// find a package that provides that command, which is then displayed to the +// user. +// - Not actually used in conhost +// Arguments: +// - string: contains the parameters that define which action we do +// Return Value: +// - false in conhost, true for the CmdNotFound action, otherwise false. +bool AdaptDispatch::DoWTAction(const std::wstring_view string) +{ + // This is not implemented in conhost. + if (_api.IsConsolePty()) + { + // Flush the frame manually to make sure this action happens at the right time. + _renderer.TriggerFlush(false); + return false; + } + + const auto parts = Utils::SplitString(string, L';'); + + if (parts.size() < 1) + { + return false; + } + + const auto action = til::at(parts, 0); + + if (action == L"CmdNotFound") + { + // The structure of the message is as follows: + // `e]9001; + // 0: CmdNotFound; + // 1: $($cmdNotFound.missingCmd); + if (parts.size() >= 2) + { + std::wstring_view missingCmd = til::at(parts, 1); + _api.SearchMissingCommand(missingCmd); + } + + return true; + } + return false; +} + // Method Description: // - DECDLD - Downloads one or more characters of a dynamically redefinable // character set (DRCS) with a specified pixel pattern. The pixel array is diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 9285d1126b7..f00960c4590 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -143,6 +143,8 @@ namespace Microsoft::Console::VirtualTerminal bool DoVsCodeAction(const std::wstring_view string) override; + bool DoWTAction(const std::wstring_view string) override; + StringHandler DownloadDRCS(const VTInt fontNumber, const VTParameter startChar, const DispatchTypes::DrcsEraseControl eraseControl, diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index e2b8f298329..ca35e4cf5f2 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -134,6 +134,8 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool DoVsCodeAction(const std::wstring_view /*string*/) override { return false; } + bool DoWTAction(const std::wstring_view /*string*/) override { return false; } + StringHandler DownloadDRCS(const VTInt /*fontNumber*/, const VTParameter /*startChar*/, const DispatchTypes::DrcsEraseControl /*eraseControl*/, diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 34d40fc850d..dac75e108d2 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -894,6 +894,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s success = _dispatch->DoVsCodeAction(string); break; } + case OscActionCodes::WTAction: + { + success = _dispatch->DoWTAction(string); + break; + } default: // If no functions to call, overall dispatch was a failure. success = false; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 1ff22ab5a99..07340d85768 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -223,6 +223,7 @@ namespace Microsoft::Console::VirtualTerminal FinalTermAction = 133, VsCodeAction = 633, ITerm2Action = 1337, + WTAction = 9001, }; bool _GetOscSetColorTable(const std::wstring_view string, From c8d13d42709a73823596ce81fb1d83d9bdc24ce5 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 23 Jan 2024 10:51:22 -0800 Subject: [PATCH 02/21] [Broken] Properly search through WinGet's catalog --- .../Microsoft.Management.Deployment.winmd | Bin 0 -> 17920 bytes .../TerminalApp/TerminalAppLib.vcxproj | 5 + src/cascadia/TerminalApp/TerminalPage.cpp | 145 ++++++++++++++++-- src/cascadia/TerminalControl/TermControl.cpp | 2 +- 4 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 src/cascadia/TerminalApp/Microsoft.Management.Deployment.winmd diff --git a/src/cascadia/TerminalApp/Microsoft.Management.Deployment.winmd b/src/cascadia/TerminalApp/Microsoft.Management.Deployment.winmd new file mode 100644 index 0000000000000000000000000000000000000000..0ed7389b5c04455624db707f86a006e337e5911c GIT binary patch literal 17920 zcmeHOdwf*YwO;$oBa_Dj5=22!5+fQxkf&gwNC>Y~B9D+Dioh^AgptWin3(_(#b6MK z2wqe~th8WwhzN>k5ydJ-s8q4iTU+^AtCsqzR;xd2tF`U@);j0xnIs^1t@qwP?&LRL z_P6%hkF)n$d#}AuQhF5~_cQFc(w0Kj89$O}tWvaTxo4)6`z9IrnJUwv$N4>ss6E66 zk(U7<=Qqs&u|(veVIE5nGS-(*`()A){ZITx`(skt>6I`SB)~Tn1GsM^@U{Dmlzj4t zhFPiqyU5REkcYd}3`R5vn%x`t<704S0yh z&V2z9RyD6sd}{#xs`^`S81-yo{blnuoYxba@F6^Q++3r-Kgne<`gxR^m*jkvKFO02**@eQ`dYBCs##Y2^ zh%mM$Wtr@f7+Wc`N?~lR%&LU3)iR692(tBhz-lNR^T+nP5^SmDu^oG;v(4C^{a`C3 zkL}t|=h=+y+d~)FjP0D+O3`F{XSP}x+r1y`ZeeWy9_ns0_5(lIk0g)%!B0JH#(v?a zzBXh3@Y5wWV?Xg@PONh5FCJ_X7GuBhgKd>M*?&A3cZ;zfG21D5>`%;g31h$VgFPXP z{mYLTdO}8t{cI&>2zlN=ct%o1{fC}0z|RE#E~va?bZ)wL26E3393)r)bjmLCUWeRU zfJw;*kSt4Myv577R??e+UixQtrQs!ayAbd+!E@V%k?sdf)(|zg2F6>wjB6#mS#V4{ zmQ)A^1?LJb2Rb#}+ioRvb^;yA{&u%R@*>cY9BH=}l3xQI$?w|T3(4Dpe;4f7{$8Ei zv;9Wo4iFpybV`kBUkUj*!K(#l3C4hq&cgOvpz~ro#woN#%WrJI9g@8s#y134iR3Q9 z9|_(k_$bh+?V0uuL;oeg*9AWo^yRR;E6~yGpYs?bqXjE~a2@Pp@1$>r4OnX*vq!)r zOY)w?%4_X)oLAsE4J)@(?ov-StjuPr%+nP!>f|l-6w3Z=wJ_6LNwV`woz>nDRz6E} zX{WW`MOZDJa$CJOrLaf6D=ACoz3jammTPHR<+63&U%dBY?X>dVNq42)Pwg~om$uuE z3HceirtKkF?xim?y)O_#3w$`QH0@=q#txg3R*3b(`W*E0D0S3~M~UME{v7o4D4k&u zFdn5&n(-)gc9@4p=}d?Dd6c?n#-nt$W;{ygXvX8xRWlx!Jk5Aq@-^d;I9D?si2}`d zB+k=}N1~f%JQC+?#v_5SOFjqvJQ9VPJ(~6zb`&S?P}-MR=N$ICv<&K@+27NYx=6E5 zKCkMjS#MvK>ZREjUk7!uX48G0Rd3Da`7)@FX3Kp__0{YiUm^9=>|tM?k^mBtgZ_gk zH$dmThjN!_mXV$zaUTl~`p-#MYLL$Bi#&vo@;T_g5_#X#Y*NNRHCVHoG77O)Tc3me zri?uGea*Oqp_;t~O^zq|bI?E2TdIahnpwNdG7dQ8bI?C3b2s)~ht*`7s#vpz%xe`# zxcotTnT6Pet^F)?sy}X1lXqRZ}#3FY7IJwPqdse^l3KHqifxnyy*g|Ah)@cAG!T;7Ff8 z2R%EHS7i~5#Iydc#n(@`#R0otM1W^z3PV!Q|#~eYR3Nl zBZtZUzCkng_l=sdzu%`B`+JjS?C+a2V}IYQ8T?EQ~u#@_#^X6*ex(Tu%+uV(E1`)p>l z{q&TB!E@mlbV&f~RUzh=A^JgXV61q#3UTztD`=f|nhpcr7?0>_IpGIuV9Ed^)c2e4WmN zZq_+o4JVR+eQlikUACLrtM58(J!xtyCx7;~Bl%_~*YL;8BiV z%ht&MA}iU}zf#gY{7Jdh2di(({0b+EwvziqzpYM7n@Js?)U?&ZBwBr|&Q3~B<$0}A zf3{1d*8g3nt$wDq^38f~J>CD$zBLZtZuSkFFFZIaq#^G2(f4syEy9UvB)%p1mf{38 z0pD_*kEY;MbPdiz0i1bEd_&Zms_~tRZ!PY0>Tnvlf%?;8d~c>4W3BfIBOjRVx)8XC z`T%FRihw&@CBUa#6M%nnv1Ye)#yvCzxCkxqP8}dVcLs=0m;vIGWq|lZ86ZAM28d6P zA>xx`hQFl}A8*zlRi#RgcMH~_BC5|`t z62}>Pi6fuA#1YSa;%H{SjN5)0xBbKs&3@uYWRGJAl~)AAl|3HAl{!5qhcpkO7373@8(Kzu2NPm@BK>gZmks0Z@TQxK3M5k ze6wJeKHP0o08gW@=@FtcMs&u~$EabpF={e}w3a5uLHn=|*Rv#bVqNwRaU$COvD66iE>2&Ms5Z z4I;l;(oK?X68+60X%@)=Ngu;q$)m1gA~}W?W){+1^V?{lv&*HpewSjoOR?OgSl@#i zfqk9=$t@6hHzmCi$wAo62KpYJ&ac@B{E-$uLWvoV;ad0a+6iUV2hF}QC9kZ<)A zK+@S&Ai3QP)-Q(KJMpQl*lBO%}C2}0h<8-%1Mkn1s}o(9ouK<*50gXG>U@+Q$~ zf~3~lgxoFO%_46``eSdi7eKYk*jo1}eb2#)m=9@p-0b$C4s;RL#@@KQ?SmWJe$X6%+u0$|9*W!6%b-0R zH>TVa#j4qA zb=vEz9y5Ou8L}qUXxi1{8#3kKzCLV#3~~utvig z8EcfRKK$>Ffc4bY2w0=>-yZ=3Z*W{Vk9qLJ{*(rE!IKQ6GJys>$pCK$^udb^co*JK zL)ro9O!$$4U!8$;j$nIuk^$|rfc%EqfL>Q%M|hNhyaM2v*f$M$-vz)Ar%!>2gRe7VL>AUr{f??lcba3xNu2#{{H%2o9h! zNG<^y=s`Jf2-XXQeoRJsD9#23`f)YzGMo_%8b;HB!x5z$=u;3l0%rz;M!`!A^ehA% zOLKrFSYHgZ9R>aXal3(A_c(AI&J+gvHy=0wal3){whMuiaK=#R0b>BSu?E(v8(Qe>!dJK3A?E$X9DZoJN`6O@^ zJq27zPXq5n%x~a%nt^L@%VyB+xKA_i7d(f6Yv~2x9k?4a@KnD5-c3h<_tJl&-SvVy z=oLsF1S<6GH%Q+v_y8S+WINEHhv_xoPI?{q5WN9>jNSt7renZI=xyNR^e*rx^geJ8 zeE@uv{shEd(*W5&47@cx3+NNy$q?Vk65q*&?R&77bpz(Y-qYYg7XmxM9@F4cJyGUd zSh^1$)CZUcTlT?62Ecm@VfE?oj!S{vVXgV}A?!4t{s7y|r*~kd`Sc#FGN1k^d*AQj z9r^STEHR({42#RBPhpSw^cie0pFW56<@J_ag3agCKVkFv_`{j2frh#U z=vLE#UKIfPR1lbfmwLtaz34|<^nsZIG(bLnh!iv)dJ5^ zb-+Be5YKa-x)FH3x&e5uS_~{y4Z!Yrb5KYZ;T=IC^-_&^+TLmfurF@-yHkJM<#(q` z)N0@$bqDZ!xQXvg!*KTOO+(dM;Befd_ofkQJ#duzF>nmdp1tV@stGt&ZN_?W70#Z0 zX(CReeQA=~4y?d^bYHqsJp>%DwqhQps@=e8>M`I9wFj$D7$?*IRH>c>&QebSXRD`y z*Ww)7pJu9NU_>1N)~KHWV>pMp>#=j%KQIe?GVs+Bq!m^H{%DzKy)1XNEoC^uJD(w? z(aPGeIh0C6!B{jNot5ZS8i)j{%xW`|=rzKu2}kQVd9wUo^Fxv9N~(?rqp@&kCY9C2 z6J|Ayn|ZAnOi*zk5eP@C#s`9PP&JK=)K*h*w7Mn`Gbfty+AuW0qVZ6|EHeYK;Oyb% z?7+NGGzNJjf*Q0|3M)$l618zEj+udksmt4K*e#B$Nra-2IE}*J6WTfP zU)>2_fMRg5L@a>SqsmN7hat@42^DfhpxR2<17KF#J#Z@OH7Z&gLA$V^l%%fE{_EnD ztX{>@a2V^1EEXjdNJV26A#?tWKGI;(yg(=%m>D+hcH$x(Va^I*?Xo-JbYZlaXgLOA z4=_Al=H^*ueYsg@JypEC2HiAu{iIX6rQ^54j$s*Y^5IL`)FuNZ<2HRdl}F}8qVpp( z7Crz^8#%W&5T?abAlG%1oZ_Xv~^PYw@wt)&9w zYhff1wnn2Qf+-ADI=ey2Fxo}}W8j{)GP_DKG58$TZaAK8E}r)K*=@tUWebHLf-Rh4F#JZ0Sr(3G2X)qVvZD;9|5Y&*e2$u|TCcDQd4%wrwY;{6q>;?TIoi)0i2JB7~e<8-gDkXOEyUmSc;< zj9ic~Bk)%7y<*;4+e#*yEo$UOw9zHY9Y@5P_Bv#HtF$SrMsW;77z+igWkNHlJgyf! zYu}I{s3=|+3685VV`8ybCR5`}$t3LbRxVQ3=9a31b%tZTR+UT4#H~8!OmGHs68;ZI z+8!H-C#+pdi|p`*%cKM^x_GM+wswl9v}i}vR=;gRqxEL0S8o=bD2}K(Q3eVfFQ&t? z+uDcQHaWabwoxpbe1?(q*M|F4(sE)ZhZ3`$cx;9aQ|;-u9=FCm7@?cC(KrRjZdPW> zHu4rlt1#$xRLHBXon)yMbFt7$u}p$uaVaZh<=BNR5v$oP*kg|EMao*Si=>NZ2XGP# zlk>Yx0mpksn+Ycdr0N#IAXhme6vwJ#woRo-E69U z3tjdaoG~Xwo$m0GhK1Yuk1?{M5Q8#0)xycp6Jk%0_?#9xTF!tTf)!_cD|O6+XU4H0R*B8c3Z9*otVe1i9$z~%%)O1J8XZ>Lh`nLj z4wAeaDMLtU(g-s?ClRd~6~j4Yel#`*5uf${jHxFM?H11NYTIa00{_l9vlhp;(m*Ue z8w!Z?@bn0OTFr zd6aXuRJ~csZFUzzfSj{YtFl`A6WZCuiXA<9N}&u?I$8}%R#NK-3Z=WDl#Zs;lPX8J zbkAhpHzvEgk!$OT279`>kJ6P_yr=>X#x?PC=hPoy34RHA$|6s3wt-&f)ITM7K%U?g zr#m_IpEx~0QcA89{EKK?xDGwbO@}zQ{s?-4B#+(^5p2uMQyA1QRPKej-OwiglI(3# z%|8Tp2tFoN{zGtw;A81BQz+b-o9LW;_+2t_yK?H^auVyg^7>ubY4R&KKR3_ln7ddR zx%F#u>R0Ec=hip26BV7-9F`7P$obhh^{@MEyUE616p5i=Tvbj^w%eOi zzu)j?8eTsq*SS4x9bSes1|LJZ$a3n}v&s&J#~Ai9JjHN;;V{GN3~#0787g?^D*@EiRP5b+zmS9`rIj8y|;&cD_n9MA5*LJzLAllGn^!qi@uJC2NNKxqSI03wAX9@>w`K zgdfj4`|KMAHQtd)l0a zwXq?K#&4>=_@0Wlw&UFsgcZHr+s0>6`E6r<7s~x8uj4lwCYe0@g#lOA^x@*8Z^5zqd2Y&5)VWA(;% Jhwpd#`7bk}-dO+u literal 0 HcmV?d00001 diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index d6abba2db90..32fc2ffa43e 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -439,6 +439,11 @@ CppWinRTImplicitlyExpandTargetPlatform True + + true + false + false + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 0924ae60df1..1931c069a57 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -11,6 +11,7 @@ #include "RequestReceiveContentArgs.g.cpp" #include +#include #include #include @@ -26,6 +27,7 @@ #include "Utils.h" 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; @@ -2954,31 +2956,148 @@ namespace winrt::TerminalApp::implementation ShowWindowChanged.raise(*this, args); } - void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs /*args*/) + void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) { - // 1. Search winget for missing command; retrieve packages - // TODO CARLOS + static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 + static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A + static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 - // TEMPORARY SOLUTION - //std::vector pkgList{ L"Microsoft.PowerToys", L"Microsoft.WindowsTerminal" }; - std::vector pkgList{ L"Microsoft.PowerToys" }; - bool tooManySuggestions = true; - std::wstring searchOption = L"id"; - std::wstring searchQuery = L"Microsoft.PowerToys"; - // END TEMPORARY SOLUTION + static constexpr unsigned int maxSuggestions = 5; + bool tooManySuggestions = false; - // 2. Display packages in UI + // TODO CARLOS: this is where we fail! "Class not registered" error + PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); + auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); + auto connectResult = catalogRef.Connect(); + int retryCount = 0; + while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) + { + connectResult = catalogRef.Connect(); + ++retryCount; + } + if (connectResult.Status() != ConnectResultStatus::Ok) + { + return; + } + auto catalog = connectResult.PackageCatalog(); + + // Perform the query (search by command) + auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); + auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); + + // Helper lambda to apply a filter to the query + auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { + // Configure filter + packageMatchFilter.Field(field); + packageMatchFilter.Option(matchOption); + packageMatchFilter.Value(query); + + // Apply filter + findPackagesOptions.ResultLimit(maxSuggestions + 1u); + findPackagesOptions.Filters().Clear(); + findPackagesOptions.Filters().Append(packageMatchFilter); + }; + + // Helper lambda to retrieve the best matching package(s) from the query's result + auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { + std::vector results; + results.reserve(std::min(matches.Size(), maxSuggestions)); + if (matches.Size() == 1) + { + // One match --> return the package + results.emplace_back(matches.GetAt(0).CatalogPackage()); + } + else if (matches.Size() > 1) + { + // Multiple matches --> display top 5 matches (prioritize best matches first) + std::queue bestExactMatches, secondaryMatches, tertiaryMatches; + for (auto match : matches) + { + switch (match.MatchCriteria().Option()) + { + case PackageFieldMatchOption::EqualsCaseInsensitive: + case PackageFieldMatchOption::Equals: + bestExactMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::StartsWithCaseInsensitive: + secondaryMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::ContainsCaseInsensitive: + tertiaryMatches.push(match.CatalogPackage()); + break; + } + } + + // Now return the top maxSuggestions + while (results.size() < maxSuggestions) + { + if (bestExactMatches.size() > 0) + { + results.emplace_back(bestExactMatches.front()); + bestExactMatches.pop(); + } + else if (secondaryMatches.size() > 0) + { + results.emplace_back(secondaryMatches.front()); + secondaryMatches.pop(); + } + else if (tertiaryMatches.size() > 0) + { + results.emplace_back(tertiaryMatches.front()); + tertiaryMatches.pop(); + } + else + { + break; + } + } + } + tooManySuggestions = matches.Size() > maxSuggestions; + return results; + }; + + // Search by command + auto missingCmd = args.MissingCommand(); + std::wstring searchOption = L"command"; + applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); + auto findPackagesResult = catalog.FindPackages(findPackagesOptions); + auto matches = findPackagesResult.Matches(); + auto pkgList = tryGetBestMatchingPackage(matches); + if (pkgList.empty()) + { + // No matches found --> search by name + applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"name"; + + if (pkgList.empty()) + { + // No matches found --> search by moniker + applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + // Perform the query (search by name) + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"moniker"; + } + } + + // Display packages in UI if (!pkgList.empty()) { std::vector suggestions; suggestions.reserve(pkgList.size()); for (auto pkg : pkgList) { - suggestions.emplace_back(fmt::format(L"winget install --{} {}", searchOption, pkg)); + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); } std::wstring footer = tooManySuggestions ? - fmt::format(L"winget search --{} {}", searchOption, searchQuery) : + fmt::format(L"winget search --{} {}", searchOption, missingCmd) : L""; ShowCommandNotFoundInfoBar(suggestions, footer); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index fbe85967716..beef3a84001 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -101,7 +101,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested }); _revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged }); _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); - _revokers.SearchMissingCommand= _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); + _revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); From 534c2d9d1ccdb97ca5216a50efc335df3ee6b967 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 8 Mar 2024 13:06:55 -0800 Subject: [PATCH 03/21] add debug code & dismiss button --- src/cascadia/TerminalApp/TerminalPage.cpp | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 1931c069a57..be494c275c6 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2958,6 +2958,13 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) { + // If the user doesn't want to see this message again, + // don't even bother trying to search for the command + if (_IsMessageDismissed(InfoBarMessage::CommandNotFound)) + { + return; + } +#if 0 static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 @@ -3102,6 +3109,24 @@ namespace winrt::TerminalApp::implementation ShowCommandNotFoundInfoBar(suggestions, footer); } +#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) + const bool tooManySuggestions = false; + const std::wstring searchOption = L"command"; + const std::wstring missingCmd = args.MissingCommand().data(); + std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); + } + + std::wstring footer = tooManySuggestions ? + fmt::format(L"winget search --{} {}", searchOption, missingCmd) : + L""; + + ShowCommandNotFoundInfoBar(suggestions, footer); +#endif } winrt::fire_and_forget TerminalPage::ShowCommandNotFoundInfoBar(const std::vector suggestions, std::wstring footerCmd) @@ -4730,6 +4755,15 @@ namespace winrt::TerminalApp::implementation } } + void TerminalPage::_CommandNotFoundDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const + { + _DismissMessage(InfoBarMessage::CommandNotFound); + if (const auto infoBar = FindName(L"CommandNotFoundInfoBar").try_as()) + { + infoBar.IsOpen(false); + } + } + // Method Description: // - Checks whether information bar message was dismissed earlier (in the application state) // Arguments: From c1e1eab81d3fc51fb5073a2232a8569df4a9af35 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 26 Mar 2024 11:14:02 -0700 Subject: [PATCH 04/21] remove info bar code; fix branch --- .../Resources/en-US/Resources.resw | 12 --- src/cascadia/TerminalApp/TerminalPage.cpp | 97 +------------------ src/cascadia/TerminalApp/TerminalPage.h | 1 - src/cascadia/TerminalControl/ControlCore.cpp | 2 +- 4 files changed, 5 insertions(+), 107 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index ccdda95446a..132abe13240 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -898,16 +898,4 @@ Restart the active pane connection - - Windows Package Manager — WinGet - - - Try installing this package using winget: - - - Additional results can be found using the following command: - - - Insert Suggestion - \ No newline at end of file diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index be494c275c6..eeb2f262e9a 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2958,12 +2958,6 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) { - // If the user doesn't want to see this message again, - // don't even bother trying to search for the command - if (_IsMessageDismissed(InfoBarMessage::CommandNotFound)) - { - return; - } #if 0 static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A @@ -3107,7 +3101,8 @@ namespace winrt::TerminalApp::implementation fmt::format(L"winget search --{} {}", searchOption, missingCmd) : L""; - ShowCommandNotFoundInfoBar(suggestions, footer); + // TODO CARLOS: no more info bar; replace! + //ShowCommandNotFoundInfoBar(suggestions, footer); } #elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) const bool tooManySuggestions = false; @@ -3125,86 +3120,11 @@ namespace winrt::TerminalApp::implementation fmt::format(L"winget search --{} {}", searchOption, missingCmd) : L""; - ShowCommandNotFoundInfoBar(suggestions, footer); + // TODO CARLOS: no more info bar; replace! + //ShowCommandNotFoundInfoBar(suggestions, footer); #endif } - winrt::fire_and_forget TerminalPage::ShowCommandNotFoundInfoBar(const std::vector suggestions, std::wstring footerCmd) - { - co_await wil::resume_foreground(Dispatcher()); - auto infoBar = FindName(L"CommandNotFoundInfoBar").try_as(); - - // Insert the message as custom content so we can make it selectable - RichTextBlock infoBarMsg; - Windows::UI::Xaml::Documents::Paragraph paragraph; - - // Append header - Windows::UI::Xaml::Documents::Run header; - header.Text(RS_(L"CommandNotFoundInfoBarHeader")); - paragraph.Inlines().Append(header); - - // Append each suggestion (and format the code blocks) - for (auto suggestion : suggestions) - { - Windows::UI::Xaml::Documents::LineBreak lineBreak; - paragraph.Inlines().Append(lineBreak); - - Windows::UI::Xaml::Documents::Run bulletPoint; - bulletPoint.Text(L"• "); - paragraph.Inlines().Append(bulletPoint); - - Windows::UI::Xaml::Documents::Run suggestionRun; - suggestionRun.Text(suggestion); - suggestionRun.FontFamily(Media::FontFamily{ L"Cascadia Code" }); - paragraph.Inlines().Append(suggestionRun); - } - - // Append the footer, if appropriate - if (!footerCmd.empty()) - { - Windows::UI::Xaml::Documents::LineBreak lineBreak; - paragraph.Inlines().Append(lineBreak); - - Windows::UI::Xaml::Documents::Run footerRun; - footerRun.Text(RS_(L"CommandNotFoundInfoBarFooter") + L" "); - paragraph.Inlines().Append(footerRun); - - Windows::UI::Xaml::Documents::Run footerCmdRun; - footerCmdRun.Text(footerCmd); - footerCmdRun.FontFamily(Media::FontFamily{ L"Cascadia Code" }); - paragraph.Inlines().Append(footerCmdRun); - } - - infoBarMsg.Blocks().Append(paragraph); - infoBarMsg.IsTextSelectionEnabled(true); - - // Set up the action button, if appropriate - // We have to use a stack panel here to force the button to be on the bottom - StackPanel infoBarContent; - infoBarContent.Children().Append(infoBarMsg); - if (suggestions.size() == 1) - { - Button infoBarBtn; - infoBarBtn.Content(box_value(RS_(L"CommandNotFoundInfoBarButtonCaption"))); - // upon clicking the action button, we inject the suggestion into the active terminal - infoBarBtn.Click([weakThis{ get_weak() }, infoBar, suggestion{ suggestions.at(0) }](auto&&, auto&&) { - if (auto page{ weakThis.get() }) - { - if (auto termControl = page->_GetActiveControl()) - { - termControl.SendInput(suggestion); - } - } - infoBar.IsOpen(false); - }); - - infoBarContent.Children().Append(infoBarBtn); - } - - infoBar.Content(infoBarContent); - infoBar.IsOpen(true); - } - // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() @@ -4755,15 +4675,6 @@ namespace winrt::TerminalApp::implementation } } - void TerminalPage::_CommandNotFoundDismissHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) const - { - _DismissMessage(InfoBarMessage::CommandNotFound); - if (const auto infoBar = FindName(L"CommandNotFoundInfoBar").try_as()) - { - infoBar.IsOpen(false); - } - } - // Method Description: // - Checks whether information bar message was dismissed earlier (in the application state) // Arguments: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index d809de20b16..a4949193bd5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -146,7 +146,6 @@ namespace winrt::TerminalApp::implementation void ShowKeyboardServiceWarning() const; winrt::hstring KeyboardServiceDisabledText(); - winrt::fire_and_forget ShowCommandNotFoundInfoBar(const std::vector suggestions, std::wstring footer); winrt::fire_and_forget IdentifyWindow(); winrt::fire_and_forget RenameFailed(); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index f0ffe9cb588..4b67b9d1cb8 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1587,7 +1587,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand) { - _SearchMissingCommandHandlers(*this, winrt::make(winrt::hstring{ missingCommand })); + SearchMissingCommand.raise(*this, winrt::make(winrt::hstring{ missingCommand })); } bool ControlCore::HasSelection() const From 68ed03d4f3c1c2b39c3f89c863e6be4f1b7bb6a8 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 26 Mar 2024 15:09:12 -0700 Subject: [PATCH 05/21] use suggestions UI instead --- .../TerminalApp/AppActionHandlers.cpp | 12 +- src/cascadia/TerminalApp/TerminalPage.cpp | 36 ++-- src/cascadia/TerminalControl/ControlCore.cpp | 167 +++++++++++++++++- src/cascadia/TerminalControl/ControlCore.h | 2 + src/cascadia/TerminalControl/ControlCore.idl | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 1 - src/cascadia/TerminalControl/TermControl.h | 2 - src/cascadia/TerminalControl/TermControl.idl | 1 - .../TerminalSettingsModel/ActionArgs.idl | 1 + .../TerminalSettingsModel/Command.cpp | 23 +++ src/cascadia/TerminalSettingsModel/Command.h | 1 + .../TerminalSettingsModel/Command.idl | 1 + 12 files changed, 231 insertions(+), 18 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index df357864c3d..b6bb51caca1 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1308,7 +1308,7 @@ namespace winrt::TerminalApp::implementation // requires context from the control) // then get that here. const bool shouldGetContext = realArgs.UseCommandline() || - WI_IsFlagSet(source, SuggestionsSource::CommandHistory); + WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::WinGetCommandNotFound); if (shouldGetContext) { if (const auto& control{ _GetActiveControl() }) @@ -1348,6 +1348,16 @@ namespace winrt::TerminalApp::implementation } } + if (WI_IsFlagSet(source, SuggestionsSource::WinGetCommandNotFound) && + context != nullptr) + { + const auto recentCommands = Command::ToSendInputCommands(context.WinGetSuggestions()); + for (const auto& t : recentCommands) + { + commandsCollection.push_back(t); + } + } + // Open the palette with all these commands in it. _OpenSuggestions(_GetActiveControl(), winrt::single_threaded_vector(std::move(commandsCollection)), diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index eeb2f262e9a..08aad606a8c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1672,7 +1672,7 @@ namespace winrt::TerminalApp::implementation term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler }); - term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); + //term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); // Don't even register for the event if the feature is compiled off. if constexpr (Feature_ShellCompletions::IsEnabled()) @@ -2956,7 +2956,7 @@ namespace winrt::TerminalApp::implementation ShowWindowChanged.raise(*this, args); } - void TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) + void TerminalPage::_SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) { #if 0 static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 @@ -3105,23 +3105,39 @@ namespace winrt::TerminalApp::implementation //ShowCommandNotFoundInfoBar(suggestions, footer); } #elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) - const bool tooManySuggestions = false; - const std::wstring searchOption = L"command"; + //const bool tooManySuggestions = false; + //const std::wstring searchOption = L"command"; const std::wstring missingCmd = args.MissingCommand().data(); std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; - std::vector suggestions; + std::vector suggestions; suggestions.reserve(pkgList.size()); for (auto pkg : pkgList) { suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); } - std::wstring footer = tooManySuggestions ? - fmt::format(L"winget search --{} {}", searchOption, missingCmd) : - L""; + // This will come in on a background (not-UI, not output) thread. + + // Parse the json string into a collection of actions + try + { + //auto commandsCollection = Command::ParsePowerShellMenuComplete(args.MenuJson(), + // args.ReplacementLength()); + + auto suggestionsWinRT = winrt::single_threaded_vector(std::move(suggestions)); + auto commandsCollection = Command::ToSendInputCommands(suggestionsWinRT); - // TODO CARLOS: no more info bar; replace! - //ShowCommandNotFoundInfoBar(suggestions, footer); + //auto weakThis{ get_weak() }; + //Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis, commandsCollection, sender]() { + // // On the UI thread... + // if (const auto& page{ weakThis.get() }) + // { + // // Open the Suggestions UI with the commands from the control + // page->_OpenSuggestions(sender.try_as(), commandsCollection, SuggestionsMode::Menu, L""); + // } + //}); + } + CATCH_LOG(); #endif } diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 4b67b9d1cb8..a77a6d2e8a7 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1585,9 +1585,168 @@ 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*/) { - SearchMissingCommand.raise(*this, winrt::make(winrt::hstring{ missingCommand })); +#if 0 + static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 + static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A + static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 + + static constexpr unsigned int maxSuggestions = 5; + bool tooManySuggestions = false; + + // TODO CARLOS: this is where we fail! "Class not registered" error + PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); + auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); + auto connectResult = catalogRef.Connect(); + int retryCount = 0; + while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) + { + connectResult = catalogRef.Connect(); + ++retryCount; + } + if (connectResult.Status() != ConnectResultStatus::Ok) + { + return; + } + auto catalog = connectResult.PackageCatalog(); + + // Perform the query (search by command) + auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); + auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); + + // Helper lambda to apply a filter to the query + auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { + // Configure filter + packageMatchFilter.Field(field); + packageMatchFilter.Option(matchOption); + packageMatchFilter.Value(query); + + // Apply filter + findPackagesOptions.ResultLimit(maxSuggestions + 1u); + findPackagesOptions.Filters().Clear(); + findPackagesOptions.Filters().Append(packageMatchFilter); + }; + + // Helper lambda to retrieve the best matching package(s) from the query's result + auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { + std::vector results; + results.reserve(std::min(matches.Size(), maxSuggestions)); + if (matches.Size() == 1) + { + // One match --> return the package + results.emplace_back(matches.GetAt(0).CatalogPackage()); + } + else if (matches.Size() > 1) + { + // Multiple matches --> display top 5 matches (prioritize best matches first) + std::queue bestExactMatches, secondaryMatches, tertiaryMatches; + for (auto match : matches) + { + switch (match.MatchCriteria().Option()) + { + case PackageFieldMatchOption::EqualsCaseInsensitive: + case PackageFieldMatchOption::Equals: + bestExactMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::StartsWithCaseInsensitive: + secondaryMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::ContainsCaseInsensitive: + tertiaryMatches.push(match.CatalogPackage()); + break; + } + } + + // Now return the top maxSuggestions + while (results.size() < maxSuggestions) + { + if (bestExactMatches.size() > 0) + { + results.emplace_back(bestExactMatches.front()); + bestExactMatches.pop(); + } + else if (secondaryMatches.size() > 0) + { + results.emplace_back(secondaryMatches.front()); + secondaryMatches.pop(); + } + else if (tertiaryMatches.size() > 0) + { + results.emplace_back(tertiaryMatches.front()); + tertiaryMatches.pop(); + } + else + { + break; + } + } + } + tooManySuggestions = matches.Size() > maxSuggestions; + return results; + }; + + // Search by command + auto missingCmd = args.MissingCommand(); + std::wstring searchOption = L"command"; + applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); + auto findPackagesResult = catalog.FindPackages(findPackagesOptions); + auto matches = findPackagesResult.Matches(); + auto pkgList = tryGetBestMatchingPackage(matches); + if (pkgList.empty()) + { + // No matches found --> search by name + applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"name"; + + if (pkgList.empty()) + { + // No matches found --> search by moniker + applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + // Perform the query (search by name) + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"moniker"; + } + } + + // Display packages in UI + if (!pkgList.empty()) + { + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); + } + + std::wstring footer = tooManySuggestions ? + fmt::format(L"winget search --{} {}", searchOption, missingCmd) : + L""; + + // TODO CARLOS: no more info bar; replace! + //ShowCommandNotFoundInfoBar(suggestions, footer); + } +#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) + //const bool tooManySuggestions = false; + //const std::wstring searchOption = L"command"; + //const std::wstring missingCmd = args.MissingCommand().data(); + std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); + } + + _cachedWinGetSuggestions = winrt::single_threaded_vector(std::move(suggestions)); +#endif } bool ControlCore::HasSelection() const @@ -2127,6 +2286,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto context = winrt::make_self(std::move(commands)); context->CurrentCommandline(winrt::hstring{ _terminal->CurrentCommand() }); + // TODO CARLOS: should we delete this after a new command is run? Or delete it after a suggestion is used? Or just after the next winget suggestion (current impl)? + // No clue which we should do. Thoughts? + context->WinGetSuggestions(_cachedWinGetSuggestions); + return *context; } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 05321644f84..51985cf25c0 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -58,6 +58,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { til::property> History; til::property CurrentCommandline; + til::property> WinGetSuggestions; CommandHistoryContext(std::vector&& history) { @@ -347,6 +348,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::point _contextMenuBufferPosition{ 0, 0 }; Windows::Foundation::Collections::IVector _cachedSearchResultRows{ nullptr }; + Windows::Foundation::Collections::IVector _cachedWinGetSuggestions{ nullptr }; void _setupDispatcherAndCallbacks(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 4c33722684b..d671d4f167f 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -62,6 +62,7 @@ namespace Microsoft.Terminal.Control { IVector History { get; }; String CurrentCommandline { get; }; + IVector WinGetSuggestions { get; }; }; [default_interface] runtimeclass ControlCore : ICoreState @@ -185,7 +186,6 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler OpenHyperlink; event Windows.Foundation.TypedEventHandler CloseTerminalRequested; event Windows.Foundation.TypedEventHandler RestartTerminalRequested; - event Windows.Foundation.TypedEventHandler SearchMissingCommand; event Windows.Foundation.TypedEventHandler Attached; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index beef3a84001..de8ec920d3e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -101,7 +101,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested }); _revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged }); _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); - _revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index e961797b894..f9deb912693 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -191,7 +191,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(SearchMissingCommand, IInspectable, Control::SearchMissingCommandEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); @@ -405,7 +404,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested; Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; - Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 183d1b9b75a..b105748ff64 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -70,7 +70,6 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler KeySent; event Windows.Foundation.TypedEventHandler CharSent; event Windows.Foundation.TypedEventHandler StringSent; - event Windows.Foundation.TypedEventHandler SearchMissingCommand; Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; }; diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 32f9005fdee..d3964cc3b81 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -119,6 +119,7 @@ namespace Microsoft.Terminal.Settings.Model Tasks = 0x1, CommandHistory = 0x2, DirectoryHistory = 0x4, + WinGetCommandNotFound = 0x8, All = 0xffffffff, }; diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 91082a4f6d7..3e875636157 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -797,4 +797,27 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return winrt::single_threaded_vector(std::move(result)); } + + IVector Command::ToSendInputCommands(IVector commands) + { + if (!commands) + { + return single_threaded_vector(); + } + + auto result = std::vector(); + for (const auto& command : commands) + { + auto args = winrt::make_self(command); + Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args }; + + auto c = winrt::make_self(); + c->_ActionAndArgs = actionAndArgs; + c->_name = command; + c->_iconPath = L"\ue74c"; // OEM icon + result.push_back(*c); + } + + return winrt::single_threaded_vector(std::move(result)); + } } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index 5e94ae721c8..c961eba352d 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -74,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static Windows::Foundation::Collections::IVector HistoryToCommands(Windows::Foundation::Collections::IVector history, winrt::hstring currentCommandline, bool directories); + static Windows::Foundation::Collections::IVector ToSendInputCommands(Windows::Foundation::Collections::IVector commands); WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index c9cec5012a4..f092325eff9 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -48,6 +48,7 @@ namespace Microsoft.Terminal.Settings.Model static IVector ParsePowerShellMenuComplete(String json, Int32 replaceLength); static IVector HistoryToCommands(IVector commandHistory, String commandline, Boolean directories); + static IVector ToSendInputCommands(IVector commands); } } From c2417bb3f58eb67c962bfcada3c7ffa11893d70b Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 26 Mar 2024 15:27:29 -0700 Subject: [PATCH 06/21] add a lil polish --- .../TerminalApp/AppActionHandlers.cpp | 3 +- .../Resources/en-US/Resources.resw | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 187 ------------------ src/cascadia/TerminalApp/TerminalPage.h | 2 - src/cascadia/TerminalControl/ControlCore.h | 1 - src/cascadia/TerminalControl/EventArgs.h | 10 - src/cascadia/TerminalControl/EventArgs.idl | 5 - .../TerminalSettingsModel/Command.cpp | 4 +- src/cascadia/TerminalSettingsModel/Command.h | 2 +- .../TerminalSettingsModel/Command.idl | 2 +- 10 files changed, 7 insertions(+), 211 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index b6bb51caca1..397f7100ea0 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1351,7 +1351,8 @@ namespace winrt::TerminalApp::implementation if (WI_IsFlagSet(source, SuggestionsSource::WinGetCommandNotFound) && context != nullptr) { - const auto recentCommands = Command::ToSendInputCommands(context.WinGetSuggestions()); + // use OEM icon + const auto recentCommands = Command::ToSendInputCommands(context.WinGetSuggestions(), hstring{ L"\ue74c" }); for (const auto& t : recentCommands) { commandsCollection.push_back(t); diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 132abe13240..3f91408815f 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -898,4 +898,4 @@ Restart the active pane connection - \ No newline at end of file + diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 08aad606a8c..9df18a9ec3d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1672,8 +1672,6 @@ namespace winrt::TerminalApp::implementation term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler }); - //term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); - // Don't even register for the event if the feature is compiled off. if constexpr (Feature_ShellCompletions::IsEnabled()) { @@ -2956,191 +2954,6 @@ namespace winrt::TerminalApp::implementation ShowWindowChanged.raise(*this, args); } - void TerminalPage::_SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) - { -#if 0 - static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 - static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A - static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 - - static constexpr unsigned int maxSuggestions = 5; - bool tooManySuggestions = false; - - // TODO CARLOS: this is where we fail! "Class not registered" error - PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); - auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); - auto connectResult = catalogRef.Connect(); - int retryCount = 0; - while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) - { - connectResult = catalogRef.Connect(); - ++retryCount; - } - if (connectResult.Status() != ConnectResultStatus::Ok) - { - return; - } - auto catalog = connectResult.PackageCatalog(); - - // Perform the query (search by command) - auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); - auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); - - // Helper lambda to apply a filter to the query - auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { - // Configure filter - packageMatchFilter.Field(field); - packageMatchFilter.Option(matchOption); - packageMatchFilter.Value(query); - - // Apply filter - findPackagesOptions.ResultLimit(maxSuggestions + 1u); - findPackagesOptions.Filters().Clear(); - findPackagesOptions.Filters().Append(packageMatchFilter); - }; - - // Helper lambda to retrieve the best matching package(s) from the query's result - auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { - std::vector results; - results.reserve(std::min(matches.Size(), maxSuggestions)); - if (matches.Size() == 1) - { - // One match --> return the package - results.emplace_back(matches.GetAt(0).CatalogPackage()); - } - else if (matches.Size() > 1) - { - // Multiple matches --> display top 5 matches (prioritize best matches first) - std::queue bestExactMatches, secondaryMatches, tertiaryMatches; - for (auto match : matches) - { - switch (match.MatchCriteria().Option()) - { - case PackageFieldMatchOption::EqualsCaseInsensitive: - case PackageFieldMatchOption::Equals: - bestExactMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::StartsWithCaseInsensitive: - secondaryMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::ContainsCaseInsensitive: - tertiaryMatches.push(match.CatalogPackage()); - break; - } - } - - // Now return the top maxSuggestions - while (results.size() < maxSuggestions) - { - if (bestExactMatches.size() > 0) - { - results.emplace_back(bestExactMatches.front()); - bestExactMatches.pop(); - } - else if (secondaryMatches.size() > 0) - { - results.emplace_back(secondaryMatches.front()); - secondaryMatches.pop(); - } - else if (tertiaryMatches.size() > 0) - { - results.emplace_back(tertiaryMatches.front()); - tertiaryMatches.pop(); - } - else - { - break; - } - } - } - tooManySuggestions = matches.Size() > maxSuggestions; - return results; - }; - - // Search by command - auto missingCmd = args.MissingCommand(); - std::wstring searchOption = L"command"; - applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); - auto findPackagesResult = catalog.FindPackages(findPackagesOptions); - auto matches = findPackagesResult.Matches(); - auto pkgList = tryGetBestMatchingPackage(matches); - if (pkgList.empty()) - { - // No matches found --> search by name - applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"name"; - - if (pkgList.empty()) - { - // No matches found --> search by moniker - applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - // Perform the query (search by name) - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"moniker"; - } - } - - // Display packages in UI - if (!pkgList.empty()) - { - std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); - } - - std::wstring footer = tooManySuggestions ? - fmt::format(L"winget search --{} {}", searchOption, missingCmd) : - L""; - - // TODO CARLOS: no more info bar; replace! - //ShowCommandNotFoundInfoBar(suggestions, footer); - } -#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) - //const bool tooManySuggestions = false; - //const std::wstring searchOption = L"command"; - const std::wstring missingCmd = args.MissingCommand().data(); - std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; - std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); - } - - // This will come in on a background (not-UI, not output) thread. - - // Parse the json string into a collection of actions - try - { - //auto commandsCollection = Command::ParsePowerShellMenuComplete(args.MenuJson(), - // args.ReplacementLength()); - - auto suggestionsWinRT = winrt::single_threaded_vector(std::move(suggestions)); - auto commandsCollection = Command::ToSendInputCommands(suggestionsWinRT); - - //auto weakThis{ get_weak() }; - //Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis, commandsCollection, sender]() { - // // On the UI thread... - // if (const auto& page{ weakThis.get() }) - // { - // // Open the Suggestions UI with the commands from the control - // page->_OpenSuggestions(sender.try_as(), commandsCollection, SuggestionsMode::Menu, L""); - // } - //}); - } - CATCH_LOG(); -#endif - } - // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index a4949193bd5..639165d1730 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -518,8 +518,6 @@ namespace winrt::TerminalApp::implementation void _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args); - void _SearchMissingCommandHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::SearchMissingCommandEventArgs args); - winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); void _onTabDragStarting(const winrt::Microsoft::UI::Xaml::Controls::TabView& sender, const winrt::Microsoft::UI::Xaml::Controls::TabViewTabDragStartingEventArgs& e); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 51985cf25c0..2965922666e 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -276,7 +276,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event UpdateSelectionMarkers; til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; - til::typed_event SearchMissingCommand; til::typed_event<> CloseTerminalRequested; til::typed_event<> RestartTerminalRequested; diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 8fc26fec6df..1c135cdf4f6 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -20,7 +20,6 @@ #include "KeySentEventArgs.g.h" #include "CharSentEventArgs.g.h" #include "StringSentEventArgs.g.h" -#include "SearchMissingCommandEventArgs.g.h" namespace winrt::Microsoft::Terminal::Control::implementation { @@ -252,15 +251,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(winrt::hstring, Text); }; - - struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT - { - public: - SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) : - _MissingCommand(missingCommand) {} - - WINRT_PROPERTY(winrt::hstring, MissingCommand); - }; } namespace winrt::Microsoft::Terminal::Control::factory_implementation diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index b9943044100..3caea5a0f38 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -121,9 +121,4 @@ namespace Microsoft.Terminal.Control { String Text { get; }; } - - runtimeclass SearchMissingCommandEventArgs - { - String MissingCommand { get; }; - } } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index 3e875636157..c7d3d00f2be 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -798,7 +798,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return winrt::single_threaded_vector(std::move(result)); } - IVector Command::ToSendInputCommands(IVector commands) + IVector Command::ToSendInputCommands(IVector commands, hstring iconPath) { if (!commands) { @@ -814,7 +814,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto c = winrt::make_self(); c->_ActionAndArgs = actionAndArgs; c->_name = command; - c->_iconPath = L"\ue74c"; // OEM icon + c->_iconPath = iconPath; result.push_back(*c); } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index c961eba352d..f926ec1606b 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -74,7 +74,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static Windows::Foundation::Collections::IVector HistoryToCommands(Windows::Foundation::Collections::IVector history, winrt::hstring currentCommandline, bool directories); - static Windows::Foundation::Collections::IVector ToSendInputCommands(Windows::Foundation::Collections::IVector commands); + static Windows::Foundation::Collections::IVector ToSendInputCommands(Windows::Foundation::Collections::IVector commands, hstring iconPath); WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index f092325eff9..48d6ee51317 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -48,7 +48,7 @@ namespace Microsoft.Terminal.Settings.Model static IVector ParsePowerShellMenuComplete(String json, Int32 replaceLength); static IVector HistoryToCommands(IVector commandHistory, String commandline, Boolean directories); - static IVector ToSendInputCommands(IVector commands); + static IVector ToSendInputCommands(IVector commands, String iconPath); } } From 6a361ef276010d085b08964e729f7844f64ac2c2 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 27 Mar 2024 16:29:30 -0700 Subject: [PATCH 07/21] address feedback; winget integration still in progress --- .../CascadiaPackage/CascadiaPackage.wapproj | 17 ++++++++++ .../TerminalApp/AppActionHandlers.cpp | 11 ++++--- .../TerminalApp/TerminalAppLib.vcxproj | 5 --- src/cascadia/TerminalApp/TerminalPage.cpp | 2 -- src/cascadia/TerminalControl/ControlCore.cpp | 8 +++-- src/cascadia/TerminalControl/ControlCore.h | 4 +-- src/cascadia/TerminalControl/ControlCore.idl | 2 +- src/cascadia/TerminalControl/EventArgs.cpp | 1 - .../Microsoft.Management.Deployment.winmd | Bin .../TerminalControlLib.vcxproj | 7 ++++ .../TerminalSettingsModel/ActionArgs.idl | 2 +- .../TerminalSettingsModel/Command.cpp | 30 ++---------------- src/cascadia/TerminalSettingsModel/Command.h | 4 +-- .../TerminalSettingsModel/Command.idl | 3 +- .../TerminalSettingsSerializationHelpers.h | 3 +- 15 files changed, 47 insertions(+), 52 deletions(-) rename src/cascadia/{TerminalApp => TerminalControl}/Microsoft.Management.Deployment.winmd (100%) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 6e43186c761..418939d420f 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -174,6 +174,23 @@ + + + + $(SolutionDir)\src\cascadia\TerminalControl + + + + Microsoft.Management.Deployment.winmd + + + + + %(WinGetAdditionalPackageFile.PackagePath) + + + + diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index 397f7100ea0..b44f249c25f 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1308,7 +1308,7 @@ namespace winrt::TerminalApp::implementation // requires context from the control) // then get that here. const bool shouldGetContext = realArgs.UseCommandline() || - WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::WinGetCommandNotFound); + WI_IsAnyFlagSet(source, SuggestionsSource::CommandHistory | SuggestionsSource::QuickFixes); if (shouldGetContext) { if (const auto& control{ _GetActiveControl() }) @@ -1341,18 +1341,19 @@ namespace winrt::TerminalApp::implementation if (WI_IsFlagSet(source, SuggestionsSource::CommandHistory) && context != nullptr) { - const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false); + // \ue81c --> History icon + const auto recentCommands = Command::HistoryToCommands(context.History(), currentCommandline, false, hstring{ L"\ue81c" }); for (const auto& t : recentCommands) { commandsCollection.push_back(t); } } - if (WI_IsFlagSet(source, SuggestionsSource::WinGetCommandNotFound) && + if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) && context != nullptr) { - // use OEM icon - const auto recentCommands = Command::ToSendInputCommands(context.WinGetSuggestions(), hstring{ L"\ue74c" }); + // \ue74c --> OEM icon + const auto recentCommands = Command::HistoryToCommands(context.QuickFixes(), hstring{ L"" }, false, hstring{ L"\ue74c" }); for (const auto& t : recentCommands) { commandsCollection.push_back(t); diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 32fc2ffa43e..d6abba2db90 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -439,11 +439,6 @@ CppWinRTImplicitlyExpandTargetPlatform True - - true - false - false - diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 9df18a9ec3d..605110a0ad7 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -11,7 +11,6 @@ #include "RequestReceiveContentArgs.g.cpp" #include -#include #include #include @@ -27,7 +26,6 @@ #include "Utils.h" 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; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index a77a6d2e8a7..e8161e258b0 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -13,6 +13,7 @@ #include #include #include +//#include #include "EventArgs.h" #include "../../buffer/out/search.h" @@ -21,6 +22,7 @@ #include "ControlCore.g.cpp" #include "SelectionColor.g.cpp" +//using namespace winrt::Microsoft::Management::Deployment; using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::VirtualTerminal; using namespace ::Microsoft::Terminal::Core; @@ -125,7 +127,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); }; _terminal->CompletionsChangedCallback(pfnCompletionsChanged); - auto pfnSearchMissingCommand = std::bind(&ControlCore::_terminalSearchMissingCommand, this, std::placeholders::_1); + auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward(PH1)); }; _terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand); // MSFT 33353327: Initialize the renderer in the ctor instead of Initialize(). @@ -1745,7 +1747,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); } - _cachedWinGetSuggestions = winrt::single_threaded_vector(std::move(suggestions)); + _cachedQuickFixes = winrt::single_threaded_vector(std::move(suggestions)); #endif } @@ -2288,7 +2290,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // TODO CARLOS: should we delete this after a new command is run? Or delete it after a suggestion is used? Or just after the next winget suggestion (current impl)? // No clue which we should do. Thoughts? - context->WinGetSuggestions(_cachedWinGetSuggestions); + context->QuickFixes(_cachedQuickFixes); return *context; } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 2965922666e..a96e6721eb6 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -58,7 +58,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { til::property> History; til::property CurrentCommandline; - til::property> WinGetSuggestions; + til::property> QuickFixes; CommandHistoryContext(std::vector&& history) { @@ -347,7 +347,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::point _contextMenuBufferPosition{ 0, 0 }; Windows::Foundation::Collections::IVector _cachedSearchResultRows{ nullptr }; - Windows::Foundation::Collections::IVector _cachedWinGetSuggestions{ nullptr }; + Windows::Foundation::Collections::IVector _cachedQuickFixes{ nullptr }; void _setupDispatcherAndCallbacks(); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index d671d4f167f..af33a9d1fd8 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -62,7 +62,7 @@ namespace Microsoft.Terminal.Control { IVector History { get; }; String CurrentCommandline { get; }; - IVector WinGetSuggestions { get; }; + IVector QuickFixes { get; }; }; [default_interface] runtimeclass ControlCore : ICoreState diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 858e2d0a81b..93e147feaa8 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -20,4 +20,3 @@ #include "KeySentEventArgs.g.cpp" #include "CharSentEventArgs.g.cpp" #include "StringSentEventArgs.g.cpp" -#include "SearchMissingCommandEventArgs.g.cpp" diff --git a/src/cascadia/TerminalApp/Microsoft.Management.Deployment.winmd b/src/cascadia/TerminalControl/Microsoft.Management.Deployment.winmd similarity index 100% rename from src/cascadia/TerminalApp/Microsoft.Management.Deployment.winmd rename to src/cascadia/TerminalControl/Microsoft.Management.Deployment.winmd diff --git a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj index 59b6a01ea7d..df495e2c22c 100644 --- a/src/cascadia/TerminalControl/TerminalControlLib.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControlLib.vcxproj @@ -182,6 +182,13 @@ false + + + true + false + false + + diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index d3964cc3b81..daaf8d792ea 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -119,7 +119,7 @@ namespace Microsoft.Terminal.Settings.Model Tasks = 0x1, CommandHistory = 0x2, DirectoryHistory = 0x4, - WinGetCommandNotFound = 0x8, + QuickFixes = 0x8, All = 0xffffffff, }; diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index c7d3d00f2be..5a18ce79a17 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -756,7 +756,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation // the command will be run as a directory change instead. IVector Command::HistoryToCommands(IVector history, winrt::hstring currentCommandline, - bool directories) + bool directories, + hstring iconPath) { std::wstring cdText = directories ? L"cd " : L""; auto result = std::vector(); @@ -788,36 +789,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation auto command = winrt::make_self(); command->_ActionAndArgs = actionAndArgs; command->_name = winrt::hstring{ line }; - command->_iconPath = directories ? - L"\ue8da" : // OpenLocal (a folder with an arrow pointing up) - L"\ue81c"; // History icon + command->_iconPath = iconPath; result.push_back(*command); foundCommands[line] = true; } return winrt::single_threaded_vector(std::move(result)); } - - IVector Command::ToSendInputCommands(IVector commands, hstring iconPath) - { - if (!commands) - { - return single_threaded_vector(); - } - - auto result = std::vector(); - for (const auto& command : commands) - { - auto args = winrt::make_self(command); - Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args }; - - auto c = winrt::make_self(); - c->_ActionAndArgs = actionAndArgs; - c->_name = command; - c->_iconPath = iconPath; - result.push_back(*c); - } - - return winrt::single_threaded_vector(std::move(result)); - } } diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h index f926ec1606b..0e83d78f815 100644 --- a/src/cascadia/TerminalSettingsModel/Command.h +++ b/src/cascadia/TerminalSettingsModel/Command.h @@ -73,8 +73,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static Windows::Foundation::Collections::IVector ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength); static Windows::Foundation::Collections::IVector HistoryToCommands(Windows::Foundation::Collections::IVector history, winrt::hstring currentCommandline, - bool directories); - static Windows::Foundation::Collections::IVector ToSendInputCommands(Windows::Foundation::Collections::IVector commands, hstring iconPath); + bool directories, + hstring iconPath); WINRT_PROPERTY(ExpandCommandType, IterateOn, ExpandCommandType::None); WINRT_PROPERTY(Model::ActionAndArgs, ActionAndArgs); diff --git a/src/cascadia/TerminalSettingsModel/Command.idl b/src/cascadia/TerminalSettingsModel/Command.idl index 48d6ee51317..bc0a146cc56 100644 --- a/src/cascadia/TerminalSettingsModel/Command.idl +++ b/src/cascadia/TerminalSettingsModel/Command.idl @@ -47,8 +47,7 @@ namespace Microsoft.Terminal.Settings.Model Windows.Foundation.Collections.IMapView NestedCommands { get; }; static IVector ParsePowerShellMenuComplete(String json, Int32 replaceLength); - static IVector HistoryToCommands(IVector commandHistory, String commandline, Boolean directories); - static IVector ToSendInputCommands(IVector commands, String iconPath); + static IVector HistoryToCommands(IVector commandHistory, String commandline, Boolean directories, String iconPath); } } diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index f1f2bc11048..c6f843398a0 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -501,11 +501,12 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FindMatchDirecti JSON_FLAG_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource) { - static constexpr std::array mappings = { + static constexpr std::array mappings = { pair_type{ "none", AllClear }, pair_type{ "tasks", ValueType::Tasks }, pair_type{ "commandHistory", ValueType::CommandHistory }, pair_type{ "directoryHistory", ValueType::DirectoryHistory }, + pair_type{ "quickFix", ValueType::QuickFixes }, pair_type{ "all", AllSet }, }; }; From cfaa09d63a9182044ca0028d33e53cefa72f2f7a Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 22 Apr 2024 17:54:03 -0700 Subject: [PATCH 08/21] moar progress! (useTransitions -> crash!) --- src/cascadia/TerminalApp/TerminalPage.cpp | 234 ++++++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 2 + src/cascadia/TerminalControl/ControlCore.cpp | 168 +------------ src/cascadia/TerminalControl/ControlCore.h | 2 + src/cascadia/TerminalControl/ControlCore.idl | 1 + src/cascadia/TerminalControl/EventArgs.cpp | 1 + src/cascadia/TerminalControl/EventArgs.h | 10 + src/cascadia/TerminalControl/EventArgs.idl | 5 + .../Resources/en-US/Resources.resw | 6 + src/cascadia/TerminalControl/TermControl.cpp | 98 +++++++- src/cascadia/TerminalControl/TermControl.h | 22 +- src/cascadia/TerminalControl/TermControl.idl | 4 + src/cascadia/TerminalControl/TermControl.xaml | 38 +++ .../TerminalSettingsModel/ActionArgs.h | 1 + .../TerminalSettingsModel/ActionArgs.idl | 2 + 15 files changed, 425 insertions(+), 169 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index ff4b6743e69..e8d7e2c49dc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -1670,6 +1670,8 @@ namespace winrt::TerminalApp::implementation term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler }); + term.SearchMissingCommand({ get_weak(), &TerminalPage::_SearchMissingCommandHandler }); + // Don't even register for the event if the feature is compiled off. if constexpr (Feature_ShellCompletions::IsEnabled()) { @@ -1688,6 +1690,12 @@ namespace winrt::TerminalApp::implementation page->_PopulateContextMenu(weakTerm.get(), sender.try_as(), true); } }); + term.QuickFixMenu().Opening([weak = get_weak(), weakTerm](auto&& sender, auto&& /*args*/) { + if (const auto& page{ weak.get() }) + { + page->_PopulateQuickFixMenu(weakTerm.get(), sender.try_as()); + } + }); } // Method Description: @@ -2889,6 +2897,180 @@ namespace winrt::TerminalApp::implementation ShowWindowChanged.raise(*this, args); } + winrt::fire_and_forget TerminalPage::_SearchMissingCommandHandler(const IInspectable /*sender*/, const Microsoft::Terminal::Control::SearchMissingCommandEventArgs args) + { + assert(!Dispatcher().HasThreadAccess()); + +#if 0 + static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 + static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A + static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 + + static constexpr unsigned int maxSuggestions = 5; + bool tooManySuggestions = false; + + // TODO CARLOS: this is where we fail! "Class not registered" error + PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); + auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); + auto connectResult = catalogRef.Connect(); + int retryCount = 0; + while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) + { + connectResult = catalogRef.Connect(); + ++retryCount; + } + if (connectResult.Status() != ConnectResultStatus::Ok) + { + return; + } + auto catalog = connectResult.PackageCatalog(); + + // Perform the query (search by command) + auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); + auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); + + // Helper lambda to apply a filter to the query + auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { + // Configure filter + packageMatchFilter.Field(field); + packageMatchFilter.Option(matchOption); + packageMatchFilter.Value(query); + + // Apply filter + findPackagesOptions.ResultLimit(maxSuggestions + 1u); + findPackagesOptions.Filters().Clear(); + findPackagesOptions.Filters().Append(packageMatchFilter); + }; + + // Helper lambda to retrieve the best matching package(s) from the query's result + auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { + std::vector results; + results.reserve(std::min(matches.Size(), maxSuggestions)); + if (matches.Size() == 1) + { + // One match --> return the package + results.emplace_back(matches.GetAt(0).CatalogPackage()); + } + else if (matches.Size() > 1) + { + // Multiple matches --> display top 5 matches (prioritize best matches first) + std::queue bestExactMatches, secondaryMatches, tertiaryMatches; + for (auto match : matches) + { + switch (match.MatchCriteria().Option()) + { + case PackageFieldMatchOption::EqualsCaseInsensitive: + case PackageFieldMatchOption::Equals: + bestExactMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::StartsWithCaseInsensitive: + secondaryMatches.push(match.CatalogPackage()); + break; + case PackageFieldMatchOption::ContainsCaseInsensitive: + tertiaryMatches.push(match.CatalogPackage()); + break; + } + } + + // Now return the top maxSuggestions + while (results.size() < maxSuggestions) + { + if (bestExactMatches.size() > 0) + { + results.emplace_back(bestExactMatches.front()); + bestExactMatches.pop(); + } + else if (secondaryMatches.size() > 0) + { + results.emplace_back(secondaryMatches.front()); + secondaryMatches.pop(); + } + else if (tertiaryMatches.size() > 0) + { + results.emplace_back(tertiaryMatches.front()); + tertiaryMatches.pop(); + } + else + { + break; + } + } + } + tooManySuggestions = matches.Size() > maxSuggestions; + return results; + }; + + // Search by command + auto missingCmd = args.MissingCommand(); + std::wstring searchOption = L"command"; + applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); + auto findPackagesResult = catalog.FindPackages(findPackagesOptions); + auto matches = findPackagesResult.Matches(); + auto pkgList = tryGetBestMatchingPackage(matches); + if (pkgList.empty()) + { + // No matches found --> search by name + applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"name"; + + if (pkgList.empty()) + { + // No matches found --> search by moniker + applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); + + // Perform the query (search by name) + findPackagesResult = catalog.FindPackages(findPackagesOptions); + matches = findPackagesResult.Matches(); + pkgList = tryGetBestMatchingPackage(matches); + searchOption = L"moniker"; + } + } + + // Display packages in UI + if (!pkgList.empty()) + { + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); + } + + std::wstring footer = tooManySuggestions ? + fmt::format(L"winget search --{} {}", searchOption, missingCmd) : + L""; + + // TODO CARLOS: no more info bar; replace! + //ShowCommandNotFoundInfoBar(suggestions, footer); + } +#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) + //const bool tooManySuggestions = false; + //const std::wstring searchOption = L"command"; + //const std::wstring missingCmd = args.MissingCommand().data(); + std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; + std::vector suggestions; + suggestions.reserve(pkgList.size()); + for (auto pkg : pkgList) + { + suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); + } + + co_await wil::resume_foreground(Dispatcher()); + + auto term = _GetActiveControl(); + if (!term) + { + co_return; + } + term.UpdateWinGetSuggestions(single_threaded_vector(std::move(suggestions))); + term.ShowQuickFixMenu(); +#endif + } + // Method Description: // - Paste text from the Windows Clipboard to the focused terminal void TerminalPage::_PasteText() @@ -4806,6 +4988,58 @@ namespace winrt::TerminalApp::implementation makeItem(RS_(L"TabClose"), L"\xE711", ActionAndArgs{ ShortcutAction::CloseTab, CloseTabArgs{ _GetFocusedTabIndex().value() } }); } + void TerminalPage::_PopulateQuickFixMenu(const TermControl& control, + const Controls::MenuFlyout& menu) + { + if (!control || !menu) + { + return; + } + + // Helper lambda for dispatching an ActionAndArgs onto the + // ShortcutActionDispatch. Used below to wire up each menu entry to the + // respective action. + + auto weak = get_weak(); + auto makeCallback = [weak](const ActionAndArgs& actionAndArgs) { + return [weak, actionAndArgs](auto&&, auto&&) { + if (auto page{ weak.get() }) + { + page->_actionDispatch->DoAction(actionAndArgs); + } + }; + }; + + auto makeItem = [&menu, &makeCallback](const winrt::hstring& label, + const winrt::hstring& icon, + const winrt::hstring& suggestion) { + MenuFlyoutItem item{}; + + if (!icon.empty()) + { + auto iconElement = UI::IconPathConverter::IconWUX(icon); + Automation::AutomationProperties::SetAccessibilityView(iconElement, Automation::Peers::AccessibilityView::Raw); + item.Icon(iconElement); + } + + item.Text(label); + item.Click(makeCallback(ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } })); + menu.Items().Append(item); + }; + + // Wire up each item to the action that should be performed. By actually + // connecting these to actions, we ensure the implementation is + // consistent. This also leaves room for customizing this menu with + // actions in the future. + + menu.Items().Clear(); + const auto quickFixes = control.CommandHistory().QuickFixes(); + for (const auto& qf : quickFixes) + { + makeItem(qf, L"\ue74c", qf); + } + } + // Handler for our WindowProperties's PropertyChanged event. We'll use this // to pop the "Identify Window" toast when the user renames our window. winrt::fire_and_forget TerminalPage::_windowPropertyChanged(const IInspectable& /*sender*/, diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 711d9f7b7ec..08530bb6672 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -510,6 +510,7 @@ 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); winrt::fire_and_forget _windowPropertyChanged(const IInspectable& sender, const winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs& args); @@ -527,6 +528,7 @@ namespace winrt::TerminalApp::implementation void _sendDraggedTabToWindow(const winrt::hstring& windowId, const uint32_t tabIndex, std::optional dragPoint); void _PopulateContextMenu(const Microsoft::Terminal::Control::TermControl& control, const Microsoft::UI::Xaml::Controls::CommandBarFlyout& sender, const bool withSelection); + void _PopulateQuickFixMenu(const Microsoft::Terminal::Control::TermControl& control, const Windows::UI::Xaml::Controls::MenuFlyout& sender); winrt::Windows::UI::Xaml::Controls::MenuFlyout _CreateRunAsAdminFlyout(int profileIndex); winrt::Microsoft::Terminal::Control::TermControl _senderOrActiveControl(const winrt::Windows::Foundation::IInspectable& sender); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 16f96e7876d..5f670de5593 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1658,168 +1658,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) { -#if 0 - static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 - static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A - static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 - - static constexpr unsigned int maxSuggestions = 5; - bool tooManySuggestions = false; - - // TODO CARLOS: this is where we fail! "Class not registered" error - PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); - auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); - auto connectResult = catalogRef.Connect(); - int retryCount = 0; - while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) - { - connectResult = catalogRef.Connect(); - ++retryCount; - } - if (connectResult.Status() != ConnectResultStatus::Ok) - { - return; - } - auto catalog = connectResult.PackageCatalog(); - - // Perform the query (search by command) - auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); - auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); - - // Helper lambda to apply a filter to the query - auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { - // Configure filter - packageMatchFilter.Field(field); - packageMatchFilter.Option(matchOption); - packageMatchFilter.Value(query); - - // Apply filter - findPackagesOptions.ResultLimit(maxSuggestions + 1u); - findPackagesOptions.Filters().Clear(); - findPackagesOptions.Filters().Append(packageMatchFilter); - }; - - // Helper lambda to retrieve the best matching package(s) from the query's result - auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { - std::vector results; - results.reserve(std::min(matches.Size(), maxSuggestions)); - if (matches.Size() == 1) - { - // One match --> return the package - results.emplace_back(matches.GetAt(0).CatalogPackage()); - } - else if (matches.Size() > 1) - { - // Multiple matches --> display top 5 matches (prioritize best matches first) - std::queue bestExactMatches, secondaryMatches, tertiaryMatches; - for (auto match : matches) - { - switch (match.MatchCriteria().Option()) - { - case PackageFieldMatchOption::EqualsCaseInsensitive: - case PackageFieldMatchOption::Equals: - bestExactMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::StartsWithCaseInsensitive: - secondaryMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::ContainsCaseInsensitive: - tertiaryMatches.push(match.CatalogPackage()); - break; - } - } - - // Now return the top maxSuggestions - while (results.size() < maxSuggestions) - { - if (bestExactMatches.size() > 0) - { - results.emplace_back(bestExactMatches.front()); - bestExactMatches.pop(); - } - else if (secondaryMatches.size() > 0) - { - results.emplace_back(secondaryMatches.front()); - secondaryMatches.pop(); - } - else if (tertiaryMatches.size() > 0) - { - results.emplace_back(tertiaryMatches.front()); - tertiaryMatches.pop(); - } - else - { - break; - } - } - } - tooManySuggestions = matches.Size() > maxSuggestions; - return results; - }; - - // Search by command - auto missingCmd = args.MissingCommand(); - std::wstring searchOption = L"command"; - applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); - auto findPackagesResult = catalog.FindPackages(findPackagesOptions); - auto matches = findPackagesResult.Matches(); - auto pkgList = tryGetBestMatchingPackage(matches); - if (pkgList.empty()) - { - // No matches found --> search by name - applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"name"; - - if (pkgList.empty()) - { - // No matches found --> search by moniker - applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - // Perform the query (search by name) - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"moniker"; - } - } - - // Display packages in UI - if (!pkgList.empty()) - { - std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); - } - - std::wstring footer = tooManySuggestions ? - fmt::format(L"winget search --{} {}", searchOption, missingCmd) : - L""; - - // TODO CARLOS: no more info bar; replace! - //ShowCommandNotFoundInfoBar(suggestions, footer); - } -#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) - //const bool tooManySuggestions = false; - //const std::wstring searchOption = L"command"; - //const std::wstring missingCmd = args.MissingCommand().data(); - std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; - std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); - } - - _cachedQuickFixes = winrt::single_threaded_vector(std::move(suggestions)); -#endif + SearchMissingCommand.raise(*this, make(hstring{ missingCommand })); } bool ControlCore::HasSelection() const @@ -2407,6 +2248,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return *context; } + void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector& quickFixes) + { + _cachedQuickFixes = quickFixes; + } + Core::Scheme ControlCore::ColorScheme() const noexcept { Core::Scheme s; diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 2816488c7a4..bfe19df3c21 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -241,6 +241,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; Control::CommandHistoryContext CommandHistory() const; + void UpdateQuickFixes(const Windows::Foundation::Collections::IVector& quickFixes); static bool IsVintageOpacityAvailable() noexcept; @@ -287,6 +288,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event UpdateSelectionMarkers; til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; + til::typed_event SearchMissingCommand; til::typed_event<> CloseTerminalRequested; til::typed_event<> RestartTerminalRequested; diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 5132f8d1749..97513f7027d 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -168,6 +168,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler TaskbarProgressChanged; event Windows.Foundation.TypedEventHandler RendererEnteredErrorState; event Windows.Foundation.TypedEventHandler ShowWindowChanged; + event Windows.Foundation.TypedEventHandler SearchMissingCommand; // These events are always called from the UI thread (bugs aside) event Windows.Foundation.TypedEventHandler FontSizeChanged; diff --git a/src/cascadia/TerminalControl/EventArgs.cpp b/src/cascadia/TerminalControl/EventArgs.cpp index 730545d42a2..123eff395db 100644 --- a/src/cascadia/TerminalControl/EventArgs.cpp +++ b/src/cascadia/TerminalControl/EventArgs.cpp @@ -19,3 +19,4 @@ #include "KeySentEventArgs.g.cpp" #include "CharSentEventArgs.g.cpp" #include "StringSentEventArgs.g.cpp" +#include "SearchMissingCommandEventArgs.g.cpp" diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index 33c215cb3bc..5b1a3464a0c 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -19,6 +19,7 @@ #include "KeySentEventArgs.g.h" #include "CharSentEventArgs.g.h" #include "StringSentEventArgs.g.h" +#include "SearchMissingCommandEventArgs.g.h" namespace winrt::Microsoft::Terminal::Control::implementation { @@ -225,6 +226,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation WINRT_PROPERTY(winrt::hstring, Text); }; + + struct SearchMissingCommandEventArgs : public SearchMissingCommandEventArgsT + { + public: + SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) : + _MissingCommand(missingCommand) {} + + WINRT_PROPERTY(winrt::hstring, MissingCommand); + }; } namespace winrt::Microsoft::Terminal::Control::factory_implementation diff --git a/src/cascadia/TerminalControl/EventArgs.idl b/src/cascadia/TerminalControl/EventArgs.idl index 958b3e15a64..a0e9489bc88 100644 --- a/src/cascadia/TerminalControl/EventArgs.idl +++ b/src/cascadia/TerminalControl/EventArgs.idl @@ -120,4 +120,9 @@ namespace Microsoft.Terminal.Control { String Text { get; }; } + + runtimeclass SearchMissingCommandEventArgs + { + String MissingCommand { get; }; + } } diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index 02245a01577..c8977790719 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -296,4 +296,10 @@ Please either install the missing font or choose another one. Select output The tooltip for a button for selecting all of a command's output + + Quick fix + + + Quick fix + \ No newline at end of file diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 7c4a5b47630..fe555439d92 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -40,6 +40,9 @@ constexpr const auto UpdatePatternLocationsInterval = std::chrono::milliseconds( // The minimum delay between emitting warning bells constexpr const auto TerminalWarningBellInterval = std::chrono::milliseconds(1000); +constexpr std::wstring_view StateNormal{ L"Normal" }; +constexpr std::wstring_view StateCollapsed{ L"Collapsed" }; + DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::CopyFormat); DEFINE_ENUM_FLAG_OPERATORS(winrt::Microsoft::Terminal::Control::MouseButtonState); @@ -101,6 +104,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested }); _revokers.CompletionsChanged = _core.CompletionsChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCompletionsChanged }); _revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested }); + _revokers.SearchMissingCommand = _core.SearchMissingCommand(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleSearchMissingCommand }); _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); @@ -214,6 +218,57 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } }); + + auto quickFixBtn{ QuickFixButton() }; + quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered }); + quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited }); + //quickFixBtn.PointerEntered([/*this*/ weakThis = get_weak()](auto&&, auto&&) { + // + // // TODO CARLOS: Tried this but *control isn't the right type + // // Error: Microsoft.Terminal.Control.dll!00007FFAA70B2AFF: ReturnHr(1) tid(a514) 80070057 The parameter is incorrect. + // // Debugger opens WindowThread.cpp; does that mean this is the wrong thread? + // if (auto control = weakThis.get()) + // { + // if (control->_quickFixButtonCollapsible) + // { + // VisualStateManager::GoToState(*control, StateNormal, true); + // } + // } + // //if (_quickFixButtonCollapsible) + // //{ + // // // TODO CARLOS: this crashes due to "invalid param" (probably the same for pointer exited) + // // VisualStateManager::GoToState(*this, StateNormal, true); + // //} + //}); + //quickFixBtn.PointerExited([/*this*/ weakThis = get_weak()](auto&&, auto&&) { + // if (auto control = weakThis.get()) + // { + // if (control->_quickFixButtonCollapsible) + // { + // VisualStateManager::GoToState(*control, StateCollapsed, true); + // } + // } + // //if (_quickFixButtonCollapsible) + // //{ + // // VisualStateManager::GoToState(*this, StateCollapsed, true); + // //} + //}); + } + + void TermControl::QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) + { + if (!_IsClosing() && _quickFixButtonCollapsible) + { + VisualStateManager::GoToState(*this, StateNormal, true); + } + } + + void TermControl::QuickFixButton_PointerExited(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) + { + if (!_IsClosing() && _quickFixButtonCollapsible) + { + VisualStateManager::GoToState(*this, StateCollapsed, true); + } } // Function Description: @@ -690,6 +745,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // When we hot reload the settings, the core will send us a scrollbar // update. If we enabled scrollbar marks, then great, when we handle // that message, we'll redraw them. + + // update the position of the quick fix menu (in case we changed the padding) + if (QuickFixButton().Visibility() == Visibility::Visible) + { + ShowQuickFixMenu(); + } } // Method Description: @@ -3401,7 +3462,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation const Control::FontSizeChangedArgs& args) { // scale the selection markers to be the size of a cell - auto scaleMarker = [args, dpiScale{ SwapChainPanel().CompositionScaleX() }](const Windows::UI::Xaml::Shapes::Path& shape) { + const auto dpiScale = SwapChainPanel().CompositionScaleX(); + auto scaleMarker = [args, &dpiScale](const Windows::UI::Xaml::Shapes::Path& shape) { // The selection markers were designed to be 5x14 in size, // so use those dimensions below for the scaling const auto scaleX = args.Width() / 5.0 / dpiScale; @@ -3417,6 +3479,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation }; scaleMarker(SelectionStartMarker()); scaleMarker(SelectionEndMarker()); + + OutputDebugString(L"Font Size:"); + OutputDebugString(std::to_wstring(QuickFixIcon().FontSize()).c_str()); + OutputDebugString(L"Args Width:"); + OutputDebugString(std::to_wstring(args.Width()).c_str()); + OutputDebugString(L"Composition Scale X:"); + OutputDebugString(std::to_wstring(dpiScale).c_str()); + + //QuickFixButton().Width(args.Width() / dpiScale); + QuickFixIcon().FontSize(args.Width() / dpiScale); } void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/, @@ -3485,6 +3557,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return _core.CommandHistory(); } + void TermControl::UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector suggestions) + { + get_self(_core)->UpdateQuickFixes(suggestions); + } + Core::Scheme TermControl::ColorScheme() const noexcept { return _core.ColorScheme(); @@ -3686,6 +3763,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation _showContextMenuAt(_toControlOrigin(cursorPos)); } + void TermControl::ShowQuickFixMenu() + { + auto quickFixBtn{ QuickFixButton() }; + + // If the gutter is narrow, display the collapsed version + const auto& termPadding = SwapChainPanel().Margin(); + + _quickFixButtonCollapsible = termPadding.Left < 16; + VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false); + + // draw the button in the gutter + Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); + + // TODO CARLOS: update for dpi? + const auto& cursorPosInDips = CursorPositionInDips(); + Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top); + quickFixBtn.Visibility(Visibility::Visible); + } + void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 24142dc8a7b..46138a0c796 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -145,6 +145,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; Control::CommandHistoryContext CommandHistory() const; + void UpdateWinGetSuggestions(Windows::Foundation::Collections::IVector suggestions); winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept; void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept; @@ -156,6 +157,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void RawWriteString(const winrt::hstring& text); void ShowContextMenu(); + void ShowQuickFixMenu(); void Detach(); @@ -184,14 +186,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE // Those attach the handler to the core directly, and will explode if // the core ever gets detached & reattached to another window. - BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); - BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); - BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TitleChanged, IInspectable, Control::TitleChangedEventArgs); + BUBBLED_FORWARDED_TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(SetTaskbarProgress, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ConnectionStateChanged, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs); + BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); + BUBBLED_FORWARDED_TYPED_EVENT(SearchMissingCommand, IInspectable, Control::SearchMissingCommandEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); @@ -219,6 +222,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool _closing{ false }; bool _focused{ false }; bool _initializedTerminal{ false }; + bool _quickFixButtonCollapsible{ false }; std::shared_ptr _playWarningBell; @@ -308,6 +312,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e); + void QuickFixButton_PointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); + void QuickFixButton_PointerExited(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); + void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); @@ -405,6 +412,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CloseTerminalRequested_revoker CloseTerminalRequested; Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; + Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index d42afeffc28..2e8e1a3b495 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -69,10 +69,12 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler KeySent; event Windows.Foundation.TypedEventHandler CharSent; event Windows.Foundation.TypedEventHandler StringSent; + event Windows.Foundation.TypedEventHandler SearchMissingCommand; Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; }; Microsoft.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; }; + Windows.UI.Xaml.Controls.MenuFlyout QuickFixMenu { get; }; event Windows.Foundation.TypedEventHandler Initialized; // This is an event handler forwarder for the underlying connection. @@ -125,6 +127,7 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); CommandHistoryContext CommandHistory(); + void UpdateWinGetSuggestions(Windows.Foundation.Collections.IVector suggestions); void AdjustOpacity(Double Opacity, Boolean relative); @@ -143,6 +146,7 @@ namespace Microsoft.Terminal.Control Windows.Foundation.Point CursorPositionInDips { get; }; void ShowContextMenu(); + void ShowQuickFixMenu(); void Detach(); } diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 043a95d3b2e..789b3d3871b 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1279,6 +1279,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index bf34a6af140..a70c9cb28d2 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -928,6 +928,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation BASIC_FACTORY(SetTabColorArgs); BASIC_FACTORY(RenameTabArgs); BASIC_FACTORY(SwapPaneArgs); + BASIC_FACTORY(SendInputArgs); BASIC_FACTORY(SplitPaneArgs); BASIC_FACTORY(SetFocusModeArgs); BASIC_FACTORY(SetFullScreenArgs); diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 7f4e930a62b..aa1bb788e93 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -226,6 +226,8 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass SendInputArgs : IActionArgs { + SendInputArgs(String input); + String Input { get; }; }; From 88b64cda6a8c46906a00c5b0bf59293fa3735f47 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 25 Apr 2024 11:27:06 -0700 Subject: [PATCH 09/21] fix design --- src/cascadia/TerminalApp/TerminalPage.cpp | 3 - src/cascadia/TerminalControl/TermControl.cpp | 73 +++++++------------ src/cascadia/TerminalControl/TermControl.h | 2 + src/cascadia/TerminalControl/TermControl.idl | 2 + src/cascadia/TerminalControl/TermControl.xaml | 42 ++++++----- 5 files changed, 54 insertions(+), 68 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e8d7e2c49dc..f1d23494aa4 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3043,9 +3043,6 @@ namespace winrt::TerminalApp::implementation std::wstring footer = tooManySuggestions ? fmt::format(L"winget search --{} {}", searchOption, missingCmd) : L""; - - // TODO CARLOS: no more info bar; replace! - //ShowCommandNotFoundInfoBar(suggestions, footer); } #elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) //const bool tooManySuggestions = false; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index fe555439d92..2041898f23e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -222,44 +222,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto quickFixBtn{ QuickFixButton() }; quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered }); quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited }); - //quickFixBtn.PointerEntered([/*this*/ weakThis = get_weak()](auto&&, auto&&) { - // - // // TODO CARLOS: Tried this but *control isn't the right type - // // Error: Microsoft.Terminal.Control.dll!00007FFAA70B2AFF: ReturnHr(1) tid(a514) 80070057 The parameter is incorrect. - // // Debugger opens WindowThread.cpp; does that mean this is the wrong thread? - // if (auto control = weakThis.get()) - // { - // if (control->_quickFixButtonCollapsible) - // { - // VisualStateManager::GoToState(*control, StateNormal, true); - // } - // } - // //if (_quickFixButtonCollapsible) - // //{ - // // // TODO CARLOS: this crashes due to "invalid param" (probably the same for pointer exited) - // // VisualStateManager::GoToState(*this, StateNormal, true); - // //} - //}); - //quickFixBtn.PointerExited([/*this*/ weakThis = get_weak()](auto&&, auto&&) { - // if (auto control = weakThis.get()) - // { - // if (control->_quickFixButtonCollapsible) - // { - // VisualStateManager::GoToState(*control, StateCollapsed, true); - // } - // } - // //if (_quickFixButtonCollapsible) - // //{ - // // VisualStateManager::GoToState(*this, StateCollapsed, true); - // //} - //}); } void TermControl::QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) { if (!_IsClosing() && _quickFixButtonCollapsible) { - VisualStateManager::GoToState(*this, StateNormal, true); + VisualStateManager::GoToState(*this, StateNormal, false); } } @@ -267,7 +236,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (!_IsClosing() && _quickFixButtonCollapsible) { - VisualStateManager::GoToState(*this, StateCollapsed, true); + VisualStateManager::GoToState(*this, StateCollapsed, false); } } @@ -2228,6 +2197,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation { _updateSelectionMarkers(nullptr, winrt::make(false)); } + + if (QuickFixButton().Visibility() == Visibility::Visible) + { + ShowQuickFixMenu(); + } } // Method Description: @@ -3480,15 +3454,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation scaleMarker(SelectionStartMarker()); scaleMarker(SelectionEndMarker()); - OutputDebugString(L"Font Size:"); - OutputDebugString(std::to_wstring(QuickFixIcon().FontSize()).c_str()); - OutputDebugString(L"Args Width:"); - OutputDebugString(std::to_wstring(args.Width()).c_str()); - OutputDebugString(L"Composition Scale X:"); - OutputDebugString(std::to_wstring(dpiScale).c_str()); - - //QuickFixButton().Width(args.Width() / dpiScale); - QuickFixIcon().FontSize(args.Width() / dpiScale); + auto quickFixBtn = QuickFixButton(); + quickFixBtn.Height(args.Height() / dpiScale); + QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); + if (quickFixBtn.Visibility() == Visibility::Visible) + { + ShowQuickFixMenu(); + } } void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/, @@ -3763,22 +3735,31 @@ namespace winrt::Microsoft::Terminal::Control::implementation _showContextMenuAt(_toControlOrigin(cursorPos)); } + double TermControl::CalculateQuickFixButtonWidth() + { + return GetPadding().Left; + } + + double TermControl::CalculateQuickFixButtonCollapsedWidth() + { + return GetPadding().Left / 3.0; + } + void TermControl::ShowQuickFixMenu() { auto quickFixBtn{ QuickFixButton() }; // If the gutter is narrow, display the collapsed version - const auto& termPadding = SwapChainPanel().Margin(); + const auto& termPadding = GetPadding(); - _quickFixButtonCollapsible = termPadding.Left < 16; + _quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width; VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false); // draw the button in the gutter Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); - // TODO CARLOS: update for dpi? const auto& cursorPosInDips = CursorPositionInDips(); - Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top); + Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y); quickFixBtn.Visibility(Visibility::Visible); } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 46138a0c796..968decf5a81 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -53,6 +53,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation float SnapDimensionToGrid(const bool widthOrHeight, const float dimension); Windows::Foundation::Point CursorPositionInDips(); + double CalculateQuickFixButtonWidth(); + double CalculateQuickFixButtonCollapsedWidth(); void WindowVisibilityChanged(const bool showOrHide); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 2e8e1a3b495..063c5350703 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -144,6 +144,8 @@ namespace Microsoft.Terminal.Control void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); Windows.Foundation.Point CursorPositionInDips { get; }; + Double CalculateQuickFixButtonWidth { get; }; + Double CalculateQuickFixButtonCollapsedWidth { get; }; void ShowContextMenu(); void ShowQuickFixMenu(); diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 789b3d3871b..ba779d1633f 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1282,19 +1282,23 @@ @@ -1377,25 +1381,25 @@ - + - - + + - + - - + + + - From d8c8807ee7722404846f69be2e6b25e215a4be51 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 26 Apr 2024 14:54:34 -0700 Subject: [PATCH 10/21] feature flag; fix scroll bug; clear QF on enter --- src/cascadia/TerminalApp/TerminalPage.cpp | 155 +----------------- src/cascadia/TerminalControl/ControlCore.cpp | 8 + src/cascadia/TerminalControl/ControlCore.h | 2 + src/cascadia/TerminalControl/ControlCore.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 56 +++++-- src/cascadia/TerminalControl/TermControl.h | 3 + src/cascadia/TerminalControl/TermControl.xaml | 10 +- src/cascadia/TerminalCore/Terminal.cpp | 63 ++++--- src/cascadia/TerminalCore/Terminal.hpp | 2 + src/features.xml | 11 ++ 10 files changed, 113 insertions(+), 198 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index dc12ea3adbc..e60604ffbb5 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2908,160 +2908,14 @@ namespace winrt::TerminalApp::implementation { assert(!Dispatcher().HasThreadAccess()); -#if 0 - static constexpr CLSID CLSID_PackageManager = { 0xC53A4F16, 0x787E, 0x42A4, 0xB3, 0x04, 0x29, 0xEF, 0xFB, 0x4B, 0xF5, 0x97 }; //C53A4F16-787E-42A4-B304-29EFFB4BF597 - static constexpr CLSID CLSID_FindPackagesOptions = { 0x572DED96, 0x9C60, 0x4526, { 0x8F, 0x92, 0xEE, 0x7D, 0x91, 0xD3, 0x8C, 0x1A } }; //572DED96-9C60-4526-8F92-EE7D91D38C1A - static constexpr CLSID CLSID_PackageMatchFilter = { 0xD02C9DAF, 0x99DC, 0x429C, { 0xB5, 0x03, 0x4E, 0x50, 0x4E, 0x4A, 0xB0, 0x00 } }; //D02C9DAF-99DC-429C-B503-4E504E4AB000 - - static constexpr unsigned int maxSuggestions = 5; - bool tooManySuggestions = false; - - // TODO CARLOS: this is where we fail! "Class not registered" error - PackageManager pkgManager = winrt::create_instance(CLSID_PackageManager, CLSCTX_ALL); - auto catalogRef = pkgManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog::OpenWindowsCatalog); - auto connectResult = catalogRef.Connect(); - int retryCount = 0; - while (connectResult.Status() != ConnectResultStatus::Ok && retryCount < 3) - { - connectResult = catalogRef.Connect(); - ++retryCount; - } - if (connectResult.Status() != ConnectResultStatus::Ok) + if (!Feature_QuickFix::IsEnabled()) { - return; - } - auto catalog = connectResult.PackageCatalog(); - - // Perform the query (search by command) - auto packageMatchFilter = winrt::create_instance(CLSID_PackageMatchFilter, CLSCTX_ALL); - auto findPackagesOptions = winrt::create_instance(CLSID_FindPackagesOptions, CLSCTX_ALL); - - // Helper lambda to apply a filter to the query - auto applyPackageMatchFilter = [&packageMatchFilter, &findPackagesOptions](PackageMatchField field, PackageFieldMatchOption matchOption, hstring query) { - // Configure filter - packageMatchFilter.Field(field); - packageMatchFilter.Option(matchOption); - packageMatchFilter.Value(query); - - // Apply filter - findPackagesOptions.ResultLimit(maxSuggestions + 1u); - findPackagesOptions.Filters().Clear(); - findPackagesOptions.Filters().Append(packageMatchFilter); - }; - - // Helper lambda to retrieve the best matching package(s) from the query's result - auto tryGetBestMatchingPackage = [&tooManySuggestions](IVectorView matches) { - std::vector results; - results.reserve(std::min(matches.Size(), maxSuggestions)); - if (matches.Size() == 1) - { - // One match --> return the package - results.emplace_back(matches.GetAt(0).CatalogPackage()); - } - else if (matches.Size() > 1) - { - // Multiple matches --> display top 5 matches (prioritize best matches first) - std::queue bestExactMatches, secondaryMatches, tertiaryMatches; - for (auto match : matches) - { - switch (match.MatchCriteria().Option()) - { - case PackageFieldMatchOption::EqualsCaseInsensitive: - case PackageFieldMatchOption::Equals: - bestExactMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::StartsWithCaseInsensitive: - secondaryMatches.push(match.CatalogPackage()); - break; - case PackageFieldMatchOption::ContainsCaseInsensitive: - tertiaryMatches.push(match.CatalogPackage()); - break; - } - } - - // Now return the top maxSuggestions - while (results.size() < maxSuggestions) - { - if (bestExactMatches.size() > 0) - { - results.emplace_back(bestExactMatches.front()); - bestExactMatches.pop(); - } - else if (secondaryMatches.size() > 0) - { - results.emplace_back(secondaryMatches.front()); - secondaryMatches.pop(); - } - else if (tertiaryMatches.size() > 0) - { - results.emplace_back(tertiaryMatches.front()); - tertiaryMatches.pop(); - } - else - { - break; - } - } - } - tooManySuggestions = matches.Size() > maxSuggestions; - return results; - }; - - // Search by command - auto missingCmd = args.MissingCommand(); - std::wstring searchOption = L"command"; - applyPackageMatchFilter(PackageMatchField::Command, PackageFieldMatchOption::StartsWithCaseInsensitive, missingCmd); - auto findPackagesResult = catalog.FindPackages(findPackagesOptions); - auto matches = findPackagesResult.Matches(); - auto pkgList = tryGetBestMatchingPackage(matches); - if (pkgList.empty()) - { - // No matches found --> search by name - applyPackageMatchFilter(PackageMatchField::Name, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"name"; - - if (pkgList.empty()) - { - // No matches found --> search by moniker - applyPackageMatchFilter(PackageMatchField::Moniker, PackageFieldMatchOption::ContainsCaseInsensitive, missingCmd); - - // Perform the query (search by name) - findPackagesResult = catalog.FindPackages(findPackagesOptions); - matches = findPackagesResult.Matches(); - pkgList = tryGetBestMatchingPackage(matches); - searchOption = L"moniker"; - } + co_return; } - // Display packages in UI - if (!pkgList.empty()) - { - std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg.Id())); - } - - std::wstring footer = tooManySuggestions ? - fmt::format(L"winget search --{} {}", searchOption, missingCmd) : - L""; - } -#elif defined(DEBUG) || defined(_DEBUG) || defined(DBG) - //const bool tooManySuggestions = false; - //const std::wstring searchOption = L"command"; - //const std::wstring missingCmd = args.MissingCommand().data(); - std::vector pkgList = { L"pkg1", L"pkg2", L"pkg3" }; std::vector suggestions; - suggestions.reserve(pkgList.size()); - for (auto pkg : pkgList) - { - suggestions.emplace_back(fmt::format(L"winget install --id {}", pkg)); - } + suggestions.reserve(1); + suggestions.emplace_back(fmt::format(L"winget install {}", args.MissingCommand())); co_await wil::resume_foreground(Dispatcher()); @@ -3072,7 +2926,6 @@ namespace winrt::TerminalApp::implementation } term.UpdateWinGetSuggestions(single_threaded_vector(std::move(suggestions))); term.ShowQuickFixMenu(); -#endif } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 340d0914ba0..0fdfbf4e079 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -115,6 +115,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward(PH1)); }; _terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand); + auto pfnClearQuickFix = [this] { _terminalClearQuickFix(); }; + _terminal->SetClearQuickFixCallback(pfnClearQuickFix); + // MSFT 33353327: Initialize the renderer in the ctor instead of Initialize(). // We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go. // If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach @@ -1622,6 +1625,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation SearchMissingCommand.raise(*this, make(hstring{ missingCommand })); } + void ControlCore::_terminalClearQuickFix() + { + ClearQuickFix.raise(*this, nullptr); + } + bool ControlCore::HasSelection() const { const auto lock = _terminal->LockForReading(); diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 447fe72744f..412aba8e097 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -287,6 +287,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; til::typed_event SearchMissingCommand; + til::typed_event<> ClearQuickFix; til::typed_event<> CloseTerminalRequested; til::typed_event<> RestartTerminalRequested; @@ -383,6 +384,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation const int velocity, const std::chrono::microseconds duration); void _terminalSearchMissingCommand(std::wstring_view missingCommand); + void _terminalClearQuickFix(); winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index b2d05036b93..0f4d5e85adf 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -176,6 +176,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RendererEnteredErrorState; event Windows.Foundation.TypedEventHandler ShowWindowChanged; event Windows.Foundation.TypedEventHandler SearchMissingCommand; + event Windows.Foundation.TypedEventHandler ClearQuickFix; // These events are always called from the UI thread (bugs aside) event Windows.Foundation.TypedEventHandler FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index ad4328fbd6e..02da86556e5 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -227,6 +227,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); + _revokers.ClearQuickFix = _core.ClearQuickFix(winrt::auto_revoke, { get_weak(), &TermControl::_clearQuickFix }); + // Initialize the terminal only once the swapchainpanel is loaded - that // way, we'll be able to query the real pixel size it got on layout _layoutUpdatedRevoker = SwapChainPanel().LayoutUpdated(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) { @@ -337,9 +339,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation } }); - auto quickFixBtn{ QuickFixButton() }; - quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered }); - quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited }); + if (Feature_QuickFix::IsEnabled()) + { + auto quickFixBtn{ QuickFixButton() }; + quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered }); + quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited }); + } } void TermControl::QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) @@ -827,10 +832,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation // update. If we enabled scrollbar marks, then great, when we handle // that message, we'll redraw them. - // update the position of the quick fix menu (in case we changed the padding) - if (QuickFixButton().Visibility() == Visibility::Visible) + if (Feature_QuickFix::IsEnabled()) { - ShowQuickFixMenu(); + // update the position of the quick fix menu (in case we changed the padding) + if (_quickFixesAvailable) + { + ShowQuickFixMenu(); + } } } @@ -2333,7 +2341,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateSelectionMarkers(nullptr, winrt::make(false)); } - if (QuickFixButton().Visibility() == Visibility::Visible) + if (_quickFixesAvailable) { ShowQuickFixMenu(); } @@ -3514,12 +3522,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation scaleMarker(SelectionStartMarker()); scaleMarker(SelectionEndMarker()); - auto quickFixBtn = QuickFixButton(); - quickFixBtn.Height(args.Height() / dpiScale); - QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); - if (quickFixBtn.Visibility() == Visibility::Visible) + if (Feature_QuickFix::IsEnabled()) { - ShowQuickFixMenu(); + auto quickFixBtn = QuickFixButton(); + quickFixBtn.Height(args.Height() / dpiScale); + QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); + if (quickFixBtn.Visibility() == Visibility::Visible) + { + ShowQuickFixMenu(); + } } } @@ -3820,6 +3831,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::ShowQuickFixMenu() { auto quickFixBtn{ QuickFixButton() }; + _quickFixesAvailable = true; // If the gutter is narrow, display the collapsed version const auto& termPadding = GetPadding(); @@ -3827,14 +3839,30 @@ namespace winrt::Microsoft::Terminal::Control::implementation _quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width; VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false); - // draw the button in the gutter - Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); + 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.BottomExclusive()) + { + quickFixBtn.Visibility(Visibility::Collapsed); + return; + } + // draw the button in the gutter const auto& cursorPosInDips = CursorPositionInDips(); + Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y); quickFixBtn.Visibility(Visibility::Visible); } + void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/) + { + _quickFixesAvailable = false; + QuickFixButton().Visibility(Visibility::Collapsed); + } + void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/, const IInspectable& /*args*/) { diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index a512a8c4698..83a75c0a71a 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -248,6 +248,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation bool _focused{ false }; bool _initializedTerminal{ false }; bool _quickFixButtonCollapsible{ false }; + bool _quickFixesAvailable{ false }; std::shared_ptr _playWarningBell; @@ -402,6 +403,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args); void _showContextMenuAt(const til::point& controlRelativePos); + void _clearQuickFix(const IInspectable& sender, const IInspectable& args); void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args); void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args); @@ -433,6 +435,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; + Control::ControlCore::ClearQuickFix_revoker ClearQuickFix; // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 0fb5e7b341f..343f449f259 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1376,15 +1376,7 @@ - - - - - + diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 34e5eb9c606..cc7d36bb8c5 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -702,34 +702,44 @@ TerminalInput::OutputType Terminal::SendCharEvent(const wchar_t ch, const WORD s vkey = _VirtualKeyFromCharacter(ch); } - // GH#1527: When the user has auto mark prompts enabled, we're going to try - // and heuristically detect if this was the line the prompt was on. - // * If the key was an Enter keypress (Terminal.app also marks ^C keypresses - // as prompts. That's omitted for now.) - // * AND we're not in the alt buffer - // - // Then treat this line like it's a prompt mark. - if (_autoMarkPrompts && vkey == VK_RETURN && !_inAltBuffer()) + if (vkey == VK_RETURN && !_inAltBuffer()) { - // We need to be a little tricky here, to try and support folks that are - // auto-marking prompts, but don't necessarily have the rest of shell - // integration enabled. - // - // We'll set the current attributes to Output, so that the output after - // here is marked as the command output. But we also need to make sure - // that a mark was started. - // We can't just check if the current row has a mark - there could be a - // multiline prompt. - // - // (TextBuffer::_createPromptMarkIfNeeded does that work for us) + // Treat VK_RETURN as a new prompt, + // so we should clear the quick fix UI if it's visible. + if (_pfnClearQuickFix) + { + _pfnClearQuickFix(); + } - const bool createdMark = _activeBuffer().StartOutput(); - if (createdMark) + // GH#1527: When the user has auto mark prompts enabled, we're going to try + // and heuristically detect if this was the line the prompt was on. + // * If the key was an Enter keypress (Terminal.app also marks ^C keypresses + // as prompts. That's omitted for now.) + // * AND we're not in the alt buffer + // + // Then treat this line like it's a prompt mark. + if (_autoMarkPrompts) { - _activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y); + // We need to be a little tricky here, to try and support folks that are + // auto-marking prompts, but don't necessarily have the rest of shell + // integration enabled. + // + // We'll set the current attributes to Output, so that the output after + // here is marked as the command output. But we also need to make sure + // that a mark was started. + // We can't just check if the current row has a mark - there could be a + // multiline prompt. + // + // (TextBuffer::_createPromptMarkIfNeeded does that work for us) + + const bool createdMark = _activeBuffer().StartOutput(); + if (createdMark) + { + _activeBuffer().ManuallyMarkRowAsPrompt(_activeBuffer().GetCursor().GetPosition().y); - // This changed the scrollbar marks - raise a notification to update them - _NotifyScrollEvent(); + // This changed the scrollbar marks - raise a notification to update them + _NotifyScrollEvent(); + } } } @@ -1235,6 +1245,11 @@ void Microsoft::Terminal::Core::Terminal::SetSearchMissingCommandCallback(std::f _pfnSearchMissingCommand.swap(pfn); } +void Microsoft::Terminal::Core::Terminal::SetClearQuickFixCallback(std::function pfn) noexcept +{ + _pfnClearQuickFix.swap(pfn); +} + // Method Description: // - Stores the search highlighted regions in the terminal void Terminal::SetSearchHighlights(const std::vector& highlights) noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index f36b6584312..6bba0eb8998 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -233,6 +233,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetPlayMidiNoteCallback(std::function pfn) noexcept; void CompletionsChangedCallback(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); @@ -343,6 +344,7 @@ class Microsoft::Terminal::Core::Terminal final : std::function _pfnPlayMidiNote; std::function _pfnCompletionsChanged; std::function _pfnSearchMissingCommand; + std::function _pfnClearQuickFix; RenderSettings _renderSettings; std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine; diff --git a/src/features.xml b/src/features.xml index c2af5ed009a..47d86790f9d 100644 --- a/src/features.xml +++ b/src/features.xml @@ -155,4 +155,15 @@ + + Feature_QuickFix + Enables the Quick Fix menu + 16599 + AlwaysDisabled + + Dev + Canary + + + From 8c0d7c6191ec66b4c84b989fdbaa279a0d0c533d Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 29 Apr 2024 14:07:24 -0700 Subject: [PATCH 11/21] clear quick fix on interaction --- src/cascadia/TerminalApp/TerminalPage.cpp | 16 +++++---- src/cascadia/TerminalControl/TermControl.cpp | 35 ++++++++++++-------- src/cascadia/TerminalControl/TermControl.h | 5 ++- src/cascadia/TerminalControl/TermControl.idl | 1 + 4 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e60604ffbb5..792efdb3572 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -4854,16 +4854,20 @@ namespace winrt::TerminalApp::implementation return; } - // Helper lambda for dispatching an ActionAndArgs onto the + // Helper lambda for dispatching a SendInput ActionAndArgs onto the // ShortcutActionDispatch. Used below to wire up each menu entry to the - // respective action. - + // respective action. Then clear the quick fix menu. auto weak = get_weak(); - auto makeCallback = [weak](const ActionAndArgs& actionAndArgs) { - return [weak, actionAndArgs](auto&&, auto&&) { + auto makeCallback = [weak](const hstring& suggestion) { + return [weak, suggestion](auto&&, auto&&) { if (auto page{ weak.get() }) { + const auto actionAndArgs = ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } }; page->_actionDispatch->DoAction(actionAndArgs); + if (auto ctrl = page->_GetActiveControl()) + { + ctrl.ClearQuickFix(); + } } }; }; @@ -4881,7 +4885,7 @@ namespace winrt::TerminalApp::implementation } item.Text(label); - item.Click(makeCallback(ActionAndArgs{ ShortcutAction::SendInput, SendInputArgs{ hstring{ L"\u0003" } + suggestion } })); + item.Click(makeCallback(suggestion)); menu.Items().Append(item); }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 02da86556e5..7ec0778cfaf 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -835,10 +835,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation if (Feature_QuickFix::IsEnabled()) { // update the position of the quick fix menu (in case we changed the padding) - if (_quickFixesAvailable) - { - ShowQuickFixMenu(); - } + ShowQuickFixMenu(); } } @@ -2341,10 +2338,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateSelectionMarkers(nullptr, winrt::make(false)); } - if (_quickFixesAvailable) - { - ShowQuickFixMenu(); - } + ShowQuickFixMenu(); } hstring TermControl::Title() @@ -3527,10 +3521,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto quickFixBtn = QuickFixButton(); quickFixBtn.Height(args.Height() / dpiScale); QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); - if (quickFixBtn.Visibility() == Visibility::Visible) - { - ShowQuickFixMenu(); - } + ShowQuickFixMenu(); } } @@ -3830,8 +3821,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::ShowQuickFixMenu() { + if (!_quickFixesAvailable) + { + QuickFixButton().Visibility(Visibility::Collapsed); + return; + } + auto quickFixBtn{ QuickFixButton() }; - _quickFixesAvailable = true; // If the gutter is narrow, display the collapsed version const auto& termPadding = GetPadding(); @@ -3857,10 +3853,21 @@ namespace winrt::Microsoft::Terminal::Control::implementation quickFixBtn.Visibility(Visibility::Visible); } + void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args) + { + _quickFixesAvailable = true; + SearchMissingCommand.raise(*this, args); + } + void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/) { _quickFixesAvailable = false; - QuickFixButton().Visibility(Visibility::Collapsed); + ShowQuickFixMenu(); + } + + void TermControl::ClearQuickFix() + { + _clearQuickFix(nullptr, nullptr); } void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/, diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 83a75c0a71a..8fc7cbbf397 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -182,6 +182,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ShowContextMenu(); void ShowQuickFixMenu(); + void ClearQuickFix(); void Detach(); @@ -206,6 +207,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event KeySent; til::typed_event CharSent; til::typed_event StringSent; + til::typed_event SearchMissingCommand; // UNDER NO CIRCUMSTANCES SHOULD YOU ADD A (PROJECTED_)FORWARDED_TYPED_EVENT HERE // Those attach the handler to the core directly, and will explode if @@ -218,7 +220,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation BUBBLED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable); BUBBLED_FORWARDED_TYPED_EVENT(CompletionsChanged, IInspectable, Control::CompletionsChangedEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable); - BUBBLED_FORWARDED_TYPED_EVENT(SearchMissingCommand, IInspectable, Control::SearchMissingCommandEventArgs); BUBBLED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs); @@ -403,6 +404,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args); void _showContextMenuAt(const til::point& controlRelativePos); + + void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args); void _clearQuickFix(const IInspectable& sender, const IInspectable& args); void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index d9336b3c11e..e49d2bb5498 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -149,6 +149,7 @@ namespace Microsoft.Terminal.Control void ShowContextMenu(); void ShowQuickFixMenu(); + void ClearQuickFix(); void Detach(); } From 767692c782107c5ac8b29bdd385740a1511cbb21 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 3 Jun 2024 15:44:38 -0700 Subject: [PATCH 12/21] remove feature flag and WinMD refs; add QuickFixesAvailable API; address some other feedback --- .../CascadiaPackage/CascadiaPackage.wapproj | 17 -------- src/cascadia/TerminalApp/TerminalPage.cpp | 7 +--- src/cascadia/TerminalControl/ControlCore.cpp | 7 +++- src/cascadia/TerminalControl/ControlCore.h | 1 + src/cascadia/TerminalControl/ControlCore.idl | 1 + src/cascadia/TerminalControl/EventArgs.h | 4 +- src/cascadia/TerminalControl/TermControl.cpp | 39 ++++++------------- src/cascadia/TerminalControl/TermControl.h | 6 +-- src/cascadia/TerminalControl/TermControl.idl | 2 +- src/cascadia/TerminalControl/TermControl.xaml | 4 +- .../TerminalControlLib.vcxproj | 7 ---- src/features.xml | 11 ------ 12 files changed, 29 insertions(+), 77 deletions(-) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 418939d420f..6e43186c761 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -174,23 +174,6 @@ - - - - $(SolutionDir)\src\cascadia\TerminalControl - - - - Microsoft.Management.Deployment.winmd - - - - - %(WinGetAdditionalPackageFile.PackagePath) - - - - diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 0f41ea0771a..af09d67f56e 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -2934,11 +2934,6 @@ namespace winrt::TerminalApp::implementation { assert(!Dispatcher().HasThreadAccess()); - if (!Feature_QuickFix::IsEnabled()) - { - co_return; - } - std::vector suggestions; suggestions.reserve(1); suggestions.emplace_back(fmt::format(L"winget install {}", args.MissingCommand())); @@ -2951,7 +2946,7 @@ namespace winrt::TerminalApp::implementation co_return; } term.UpdateWinGetSuggestions(single_threaded_vector(std::move(suggestions))); - term.ShowQuickFixMenu(); + term.RefreshQuickFixMenu(); } // Method Description: diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index e48809d0b8c..98ad193b598 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -13,7 +13,6 @@ #include #include #include -//#include #include "EventArgs.h" #include "../../renderer/atlas/AtlasEngine.h" @@ -23,7 +22,6 @@ #include "ControlCore.g.cpp" #include "SelectionColor.g.cpp" -//using namespace winrt::Microsoft::Management::Deployment; using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Console::VirtualTerminal; using namespace ::Microsoft::Terminal::Core; @@ -2304,6 +2302,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation return *context; } + bool ControlCore::QuickFixesAvailable() const noexcept + { + return _cachedQuickFixes && _cachedQuickFixes.Size() > 0; + } + void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector& quickFixes) { _cachedQuickFixes = quickFixes; diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index ea4e6de4b7f..e3e8943989d 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -242,6 +242,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation hstring ReadEntireBuffer() const; Control::CommandHistoryContext CommandHistory() const; + bool QuickFixesAvailable() const noexcept; void UpdateQuickFixes(const Windows::Foundation::Collections::IVector& quickFixes); void AdjustOpacity(const float opacity, const bool relative); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 079cbb42e64..384cc681a77 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -156,6 +156,7 @@ namespace Microsoft.Terminal.Control String ReadEntireBuffer(); CommandHistoryContext CommandHistory(); + Boolean QuickFixesAvailable { get; }; void AdjustOpacity(Single Opacity, Boolean relative); void WindowVisibilityChanged(Boolean showOrHide); diff --git a/src/cascadia/TerminalControl/EventArgs.h b/src/cascadia/TerminalControl/EventArgs.h index a0ba3f5c6e1..6770da9f62d 100644 --- a/src/cascadia/TerminalControl/EventArgs.h +++ b/src/cascadia/TerminalControl/EventArgs.h @@ -217,9 +217,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation { public: SearchMissingCommandEventArgs(const winrt::hstring& missingCommand) : - _MissingCommand(missingCommand) {} + MissingCommand(missingCommand) {} - WINRT_PROPERTY(winrt::hstring, MissingCommand); + til::property MissingCommand; }; } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 2f2417064a8..c5672802d18 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -338,16 +338,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } }); - - if (Feature_QuickFix::IsEnabled()) - { - auto quickFixBtn{ QuickFixButton() }; - quickFixBtn.PointerEntered({ get_weak(), &TermControl::QuickFixButton_PointerEntered }); - quickFixBtn.PointerExited({ get_weak(), &TermControl::QuickFixButton_PointerExited }); - } } - void TermControl::QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) + void TermControl::_QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) { if (!_IsClosing() && _quickFixButtonCollapsible) { @@ -355,7 +348,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation } } - void TermControl::QuickFixButton_PointerExited(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) + void TermControl::_QuickFixButton_PointerExited(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/) { if (!_IsClosing() && _quickFixButtonCollapsible) { @@ -838,11 +831,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation // update. If we enabled scrollbar marks, then great, when we handle // that message, we'll redraw them. - if (Feature_QuickFix::IsEnabled()) - { - // update the position of the quick fix menu (in case we changed the padding) - ShowQuickFixMenu(); - } + // update the position of the quick fix menu (in case we changed the padding) + RefreshQuickFixMenu(); } // Method Description: @@ -2349,7 +2339,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateSelectionMarkers(nullptr, winrt::make(false)); } - ShowQuickFixMenu(); + RefreshQuickFixMenu(); } hstring TermControl::Title() @@ -3527,13 +3517,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation scaleMarker(SelectionStartMarker()); scaleMarker(SelectionEndMarker()); - if (Feature_QuickFix::IsEnabled()) - { - auto quickFixBtn = QuickFixButton(); - quickFixBtn.Height(args.Height() / dpiScale); - QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); - ShowQuickFixMenu(); - } + auto quickFixBtn = QuickFixButton(); + quickFixBtn.Height(args.Height() / dpiScale); + QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); + RefreshQuickFixMenu(); } void TermControl::_coreRaisedNotice(const IInspectable& /*sender*/, @@ -3831,9 +3818,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation return GetPadding().Left / 3.0; } - void TermControl::ShowQuickFixMenu() + void TermControl::RefreshQuickFixMenu() { - if (!_quickFixesAvailable) + if (!_core.QuickFixesAvailable()) { QuickFixButton().Visibility(Visibility::Collapsed); return; @@ -3867,14 +3854,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args) { - _quickFixesAvailable = true; SearchMissingCommand.raise(*this, args); } void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/) { - _quickFixesAvailable = false; - ShowQuickFixMenu(); + RefreshQuickFixMenu(); } void TermControl::ClearQuickFix() diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 254ae88c068..2e8f51759fd 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -181,7 +181,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void RawWriteString(const winrt::hstring& text); void ShowContextMenu(); - void ShowQuickFixMenu(); + void RefreshQuickFixMenu(); void ClearQuickFix(); void Detach(); @@ -340,8 +340,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _MouseWheelHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); void _ScrollbarChangeHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs& e); - void QuickFixButton_PointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); - void QuickFixButton_PointerExited(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); + void _QuickFixButton_PointerEntered(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); + void _QuickFixButton_PointerExited(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e); void _GotFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); void _LostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& e); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index e49d2bb5498..19b087b1045 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -148,7 +148,7 @@ namespace Microsoft.Terminal.Control Double CalculateQuickFixButtonCollapsedWidth { get; }; void ShowContextMenu(); - void ShowQuickFixMenu(); + void RefreshQuickFixMenu(); void ClearQuickFix(); void Detach(); diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 343f449f259..7bbdc577ca2 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1289,7 +1289,9 @@ AllowFocusOnInteraction="False" CornerRadius="0,5,5,0" Style="{StaticResource AccentButtonStyle}" - Visibility="Collapsed"> + Visibility="Collapsed" + PointerEntered="_QuickFixButton_PointerEntered" + PointerExited="_QuickFixButton_PointerExited"> false - - - true - false - false - - diff --git a/src/features.xml b/src/features.xml index 47d86790f9d..c2af5ed009a 100644 --- a/src/features.xml +++ b/src/features.xml @@ -155,15 +155,4 @@ - - Feature_QuickFix - Enables the Quick Fix menu - 16599 - AlwaysDisabled - - Dev - Canary - - - From d4b6904ff7bca2dc1c63af4c4310b13de9624a63 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 4 Jun 2024 17:20:24 -0700 Subject: [PATCH 13/21] improve clearing functionality; fix build; x:Load; try to fix sizing --- .../TerminalApp/AppActionHandlers.cpp | 3 ++- src/cascadia/TerminalControl/ControlCore.cpp | 10 +++---- src/cascadia/TerminalControl/ControlCore.h | 8 +++--- src/cascadia/TerminalControl/ControlCore.idl | 4 ++- src/cascadia/TerminalControl/TermControl.cpp | 26 +++++++++---------- src/cascadia/TerminalControl/TermControl.h | 3 +-- src/cascadia/TerminalControl/TermControl.xaml | 7 ++--- src/terminal/adapter/adaptDispatch.cpp | 2 +- .../adapter/ut_adapter/adapterTest.cpp | 5 ++++ 9 files changed, 38 insertions(+), 30 deletions(-) diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index b556f6e80d4..724f7dafb70 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -1382,7 +1382,8 @@ namespace winrt::TerminalApp::implementation } if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) && - context != nullptr) + context != nullptr && + context.QuickFixes() != nullptr) { // \ue74c --> OEM icon const auto recentCommands = Command::HistoryToCommands(context.QuickFixes(), hstring{ L"" }, false, hstring{ L"\ue74c" }); diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 98ad193b598..b4a155070eb 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -113,7 +113,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward(PH1)); }; _terminal->SetSearchMissingCommandCallback(pfnSearchMissingCommand); - auto pfnClearQuickFix = [this] { _terminalClearQuickFix(); }; + auto pfnClearQuickFix = [this] { ClearQuickFix(); }; _terminal->SetClearQuickFixCallback(pfnClearQuickFix); // MSFT 33353327: Initialize the renderer in the ctor instead of Initialize(). @@ -1620,9 +1620,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation SearchMissingCommand.raise(*this, make(hstring{ missingCommand })); } - void ControlCore::_terminalClearQuickFix() + void ControlCore::ClearQuickFix() { - ClearQuickFix.raise(*this, nullptr); + _cachedQuickFixes = nullptr; + RefreshQuickFixUI.raise(*this, nullptr); } bool ControlCore::HasSelection() const @@ -2293,11 +2294,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation commands.pop_back(); } - auto context = winrt::make_self(std::move(commands)); context->CurrentCommandline(trimmedCurrentCommand); - // TODO CARLOS: should we delete this after a new command is run? Or delete it after a suggestion is used? Or just after the next winget suggestion (current impl)? - // No clue which we should do. Thoughts? context->QuickFixes(_cachedQuickFixes); return *context; } diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index e3e8943989d..f5ce6fa9a2d 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -70,7 +70,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::property CurrentCommandline; til::property> QuickFixes; - CommandHistoryContext(std::vector&& history) + CommandHistoryContext(std::vector&& history) : + QuickFixes(winrt::single_threaded_vector()) { History(winrt::single_threaded_vector(std::move(history))); } @@ -154,6 +155,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation void PersistToPath(const wchar_t* path) const; void RestoreFromPath(const wchar_t* path) const; + void ClearQuickFix(); + #pragma region ICoreState const size_t TaskbarState() const noexcept; const size_t TaskbarProgress() const noexcept; @@ -287,7 +290,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation til::typed_event OpenHyperlink; til::typed_event CompletionsChanged; til::typed_event SearchMissingCommand; - til::typed_event<> ClearQuickFix; + til::typed_event<> RefreshQuickFixUI; til::typed_event<> CloseTerminalRequested; til::typed_event<> RestartTerminalRequested; @@ -384,7 +387,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation const int velocity, const std::chrono::microseconds duration); void _terminalSearchMissingCommand(std::wstring_view missingCommand); - void _terminalClearQuickFix(); winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl index 384cc681a77..c4a09a42122 100644 --- a/src/cascadia/TerminalControl/ControlCore.idl +++ b/src/cascadia/TerminalControl/ControlCore.idl @@ -168,6 +168,8 @@ namespace Microsoft.Terminal.Control Boolean ShouldShowSelectCommand(); Boolean ShouldShowSelectOutput(); + void ClearQuickFix(); + // These events are called from some background thread event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler WarningBell; @@ -177,7 +179,7 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler RendererEnteredErrorState; event Windows.Foundation.TypedEventHandler ShowWindowChanged; event Windows.Foundation.TypedEventHandler SearchMissingCommand; - event Windows.Foundation.TypedEventHandler ClearQuickFix; + event Windows.Foundation.TypedEventHandler RefreshQuickFixUI; // These events are always called from the UI thread (bugs aside) event Windows.Foundation.TypedEventHandler FontSizeChanged; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c5672802d18..a8df2d27fe3 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -227,7 +227,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation _revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard }); - _revokers.ClearQuickFix = _core.ClearQuickFix(winrt::auto_revoke, { get_weak(), &TermControl::_clearQuickFix }); + _revokers.RefreshQuickFixUI = _core.RefreshQuickFixUI(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) { + RefreshQuickFixMenu(); + }); // Initialize the terminal only once the swapchainpanel is loaded - that // way, we'll be able to query the real pixel size it got on layout @@ -3517,7 +3519,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation scaleMarker(SelectionStartMarker()); scaleMarker(SelectionEndMarker()); - auto quickFixBtn = QuickFixButton(); + auto quickFixBtn = FindName(L"QuickFixButton").as(); quickFixBtn.Height(args.Height() / dpiScale); QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); RefreshQuickFixMenu(); @@ -3810,24 +3812,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation double TermControl::CalculateQuickFixButtonWidth() { + if (_quickFixButtonCollapsible) + { + return gsl::narrow_cast(CharacterDimensions().Width); + } return GetPadding().Left; } double TermControl::CalculateQuickFixButtonCollapsedWidth() { - return GetPadding().Left / 3.0; + return std::max(4.0, GetPadding().Left / 3.0); } void TermControl::RefreshQuickFixMenu() { + auto quickFixBtn = FindName(L"QuickFixButton").as(); if (!_core.QuickFixesAvailable()) { - QuickFixButton().Visibility(Visibility::Collapsed); + quickFixBtn.Visibility(Visibility::Collapsed); return; } - auto quickFixBtn{ QuickFixButton() }; - // If the gutter is narrow, display the collapsed version const auto& termPadding = GetPadding(); @@ -3848,7 +3853,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // draw the button in the gutter const auto& cursorPosInDips = CursorPositionInDips(); Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); - Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y); + Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top); quickFixBtn.Visibility(Visibility::Visible); } @@ -3857,14 +3862,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation SearchMissingCommand.raise(*this, args); } - void TermControl::_clearQuickFix(const IInspectable& /*sender*/, const IInspectable& /*args*/) - { - RefreshQuickFixMenu(); - } - void TermControl::ClearQuickFix() { - _clearQuickFix(nullptr, nullptr); + _core.ClearQuickFix(); } void TermControl::_PasteCommandHandler(const IInspectable& /*sender*/, diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 2e8f51759fd..92e381bb56a 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -406,7 +406,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation void _showContextMenuAt(const til::point& controlRelativePos); void _bubbleSearchMissingCommand(const IInspectable& sender, const Control::SearchMissingCommandEventArgs& args); - void _clearQuickFix(const IInspectable& sender, const IInspectable& args); void _PasteCommandHandler(const IInspectable& sender, const IInspectable& args); void _CopyCommandHandler(const IInspectable& sender, const IInspectable& args); @@ -438,7 +437,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation Control::ControlCore::CompletionsChanged_revoker CompletionsChanged; Control::ControlCore::RestartTerminalRequested_revoker RestartTerminalRequested; Control::ControlCore::SearchMissingCommand_revoker SearchMissingCommand; - Control::ControlCore::ClearQuickFix_revoker ClearQuickFix; + Control::ControlCore::RefreshQuickFixUI_revoker RefreshQuickFixUI; // These are set up in _InitializeTerminal Control::ControlCore::RendererWarning_revoker RendererWarning; diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 7bbdc577ca2..be90214566d 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1286,12 +1286,13 @@ Padding="0" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" + x:Load="False" AllowFocusOnInteraction="False" CornerRadius="0,5,5,0" - Style="{StaticResource AccentButtonStyle}" - Visibility="Collapsed" PointerEntered="_QuickFixButton_PointerEntered" - PointerExited="_QuickFixButton_PointerExited"> + PointerExited="_QuickFixButton_PointerExited" + Style="{StaticResource AccentButtonStyle}" + Visibility="Collapsed"> = 2) { - std::wstring_view missingCmd = til::at(parts, 1); + const std::wstring_view missingCmd = til::at(parts, 1); _api.SearchMissingCommand(missingCmd); } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index c0ff50e5748..2d3abdaf3d8 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -218,6 +218,11 @@ class TestGetSet final : public ITerminalApi VERIFY_ARE_EQUAL(_expectedReplaceLength, replaceLength); } + void SearchMissingCommand(const std::wstring_view /*command*/) override + { + Log::Comment(L"SearchMissingCommand MOCK called..."); + } + void PrepData() { PrepData(CursorDirection::UP); // if called like this, the cursor direction doesn't matter. From 4e5d07dc48ca19ea6fcb633f688cb7c47c43f909 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 4 Jun 2024 17:34:12 -0700 Subject: [PATCH 14/21] accessibility! --- .../TerminalControl/Resources/en-US/Resources.resw | 9 ++++++++- src/cascadia/TerminalControl/TermControl.cpp | 9 +++++++++ src/cascadia/TerminalControl/TermControl.xaml | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw index a2075fe3c8c..ee724ba88c8 100644 --- a/src/cascadia/TerminalControl/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalControl/Resources/en-US/Resources.resw @@ -318,4 +318,11 @@ Please either install the missing font or choose another one. invalid This brief message is displayed when a regular expression is invalid. - + + Quick fix available + "Quick fix" is referencing the same feature as "QuickFixButton.ToolTipService.ToolTip". + + + Quick fix + + \ No newline at end of file diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a8df2d27fe3..60f9d45693e 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -3855,6 +3855,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation Controls::Canvas::SetLeft(quickFixBtn, -termPadding.Left); Controls::Canvas::SetTop(quickFixBtn, cursorPosInDips.Y - termPadding.Top); quickFixBtn.Visibility(Visibility::Visible); + + if (auto automationPeer{ FrameworkElementAutomationPeer::FromElement(*this) }) + { + automationPeer.RaiseNotificationEvent( + AutomationNotificationKind::ItemAdded, + AutomationNotificationProcessing::ImportantMostRecent, + RS_(L"QuickFixAvailable"), + L"QuickFixAvailableAnnouncement" /* unique name for this group of notifications */); + } } void TermControl::_bubbleSearchMissingCommand(const IInspectable& /*sender*/, const Control::SearchMissingCommandEventArgs& args) diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index be90214566d..fb5d06df84e 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1301,7 +1301,8 @@ Glyph="" /> - + From 27d464c357326ced9cd4ac6c1dd712cd2818a5a1 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 5 Jun 2024 15:07:41 -0700 Subject: [PATCH 15/21] adjust button sizing for better visibility --- src/cascadia/TerminalControl/TermControl.cpp | 21 +++++++++++++------ src/cascadia/TerminalControl/TermControl.h | 4 ++-- src/cascadia/TerminalControl/TermControl.idl | 4 ++-- src/cascadia/TerminalControl/TermControl.xaml | 4 ++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 60f9d45693e..150a4d9d86d 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -3521,7 +3521,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation auto quickFixBtn = FindName(L"QuickFixButton").as(); quickFixBtn.Height(args.Height() / dpiScale); - QuickFixIcon().FontSize(std::min(static_cast(args.Width() / dpiScale), GetPadding().Left)); + QuickFixIcon().FontSize(std::max(static_cast(args.Width() / dpiScale), GetPadding().Left)); RefreshQuickFixMenu(); } @@ -3810,18 +3810,24 @@ namespace winrt::Microsoft::Terminal::Control::implementation _showContextMenuAt(_toControlOrigin(cursorPos)); } - double TermControl::CalculateQuickFixButtonWidth() + double TermControl::QuickFixButtonWidth() { + const auto leftPadding = GetPadding().Left; if (_quickFixButtonCollapsible) { - return gsl::narrow_cast(CharacterDimensions().Width); + const auto cellWidth = CharacterDimensions().Width; + if (leftPadding == 0) + { + return cellWidth; + } + return leftPadding + (cellWidth / 2.0); } - return GetPadding().Left; + return leftPadding; } - double TermControl::CalculateQuickFixButtonCollapsedWidth() + double TermControl::QuickFixButtonCollapsedWidth() { - return std::max(4.0, GetPadding().Left / 3.0); + return std::max(CharacterDimensions().Width * 2.0 / 3.0, GetPadding().Left); } void TermControl::RefreshQuickFixMenu() @@ -3836,7 +3842,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation // If the gutter is narrow, display the collapsed version const auto& termPadding = GetPadding(); + // Make sure to update _quickFixButtonCollapsible and QuickFix button widths BEFORE updating the VisualState _quickFixButtonCollapsible = termPadding.Left < CharacterDimensions().Width; + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"QuickFixButtonWidth" }); + PropertyChanged.raise(*this, Windows::UI::Xaml::Data::PropertyChangedEventArgs{ L"QuickFixButtonCollapsedWidth" }); VisualStateManager::GoToState(*this, !_quickFixButtonCollapsible ? StateNormal : StateCollapsed, false); const auto rd = get_self(_core)->GetRenderData(); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 92e381bb56a..6e8d91d651c 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -75,8 +75,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation float SnapDimensionToGrid(const bool widthOrHeight, const float dimension); Windows::Foundation::Point CursorPositionInDips(); - double CalculateQuickFixButtonWidth(); - double CalculateQuickFixButtonCollapsedWidth(); + double QuickFixButtonWidth(); + double QuickFixButtonCollapsedWidth(); void WindowVisibilityChanged(const bool showOrHide); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 19b087b1045..82286dbe5fd 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -144,8 +144,8 @@ namespace Microsoft.Terminal.Control void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode); Windows.Foundation.Point CursorPositionInDips { get; }; - Double CalculateQuickFixButtonWidth { get; }; - Double CalculateQuickFixButtonCollapsedWidth { get; }; + Double QuickFixButtonWidth { get; }; + Double QuickFixButtonCollapsedWidth { get; }; void ShowContextMenu(); void RefreshQuickFixMenu(); diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index fb5d06df84e..bcbabf0daf4 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -1282,7 +1282,7 @@