-
Notifications
You must be signed in to change notification settings - Fork 9k
Add Quick Fix UI and support for custom CommandNotFound OSC #16848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 13 commits
12100c5
c8d13d4
534c2d9
c1e1eab
68ed03d
c2417bb
6a361ef
a2e57b4
cfaa09d
88b64cd
48a36be
d8c8807
8c0d7c6
ec1fbc2
767692c
d4b6904
4e5d07d
27d464c
0f801a9
a545325
755f121
a0aa2d0
1ffbae1
0c5d880
ef6af81
db7f464
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1671,6 +1671,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()) | ||
| { | ||
|
|
@@ -1689,6 +1691,12 @@ namespace winrt::TerminalApp::implementation | |
| page->_PopulateContextMenu(weakTerm.get(), sender.try_as<MUX::Controls::CommandBarFlyout>(), 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<Controls::MenuFlyout>()); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // Method Description: | ||
|
|
@@ -2896,6 +2904,30 @@ 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 (!Feature_QuickFix::IsEnabled()) | ||
| { | ||
| co_return; | ||
| } | ||
|
|
||
| std::vector<hstring> suggestions; | ||
| suggestions.reserve(1); | ||
| suggestions.emplace_back(fmt::format(L"winget install {}", args.MissingCommand())); | ||
|
||
|
|
||
| co_await wil::resume_foreground(Dispatcher()); | ||
|
|
||
| auto term = _GetActiveControl(); | ||
| if (!term) | ||
| { | ||
| co_return; | ||
| } | ||
| term.UpdateWinGetSuggestions(single_threaded_vector<hstring>(std::move(suggestions))); | ||
| term.ShowQuickFixMenu(); | ||
| } | ||
|
|
||
| // Method Description: | ||
| // - Paste text from the Windows Clipboard to the focused terminal | ||
| void TerminalPage::_PasteText() | ||
|
|
@@ -4814,6 +4846,62 @@ 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 a SendInput ActionAndArgs onto the | ||
| // ShortcutActionDispatch. Used below to wire up each menu entry to the | ||
| // respective action. Then clear the quick fix menu. | ||
| auto weak = get_weak(); | ||
| auto makeCallback = [weak](const hstring& suggestion) { | ||
carlos-zamora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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(); | ||
| } | ||
| } | ||
| }; | ||
| }; | ||
|
|
||
| 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(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*/, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| #include <unicode.hpp> | ||
| #include <utils.hpp> | ||
| #include <WinUser.h> | ||
| //#include <winrt/Microsoft.Management.Deployment.h> | ||
carlos-zamora marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| #include "EventArgs.h" | ||
| #include "../../renderer/atlas/AtlasEngine.h" | ||
|
|
@@ -22,6 +23,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; | ||
|
|
@@ -110,6 +112,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| auto pfnCompletionsChanged = [=](auto&& menuJson, auto&& replaceLength) { _terminalCompletionsChanged(menuJson, replaceLength); }; | ||
| _terminal->CompletionsChangedCallback(pfnCompletionsChanged); | ||
|
|
||
| auto pfnSearchMissingCommand = [this](auto&& PH1) { _terminalSearchMissingCommand(std::forward<decltype(PH1)>(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 | ||
|
|
@@ -1612,6 +1620,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| _midiAudio.PlayNote(reinterpret_cast<HWND>(_owningHwnd), noteNumber, velocity, std::chrono::duration_cast<std::chrono::milliseconds>(duration)); | ||
| } | ||
|
|
||
| void ControlCore::_terminalSearchMissingCommand(std::wstring_view missingCommand) | ||
| { | ||
| SearchMissingCommand.raise(*this, make<implementation::SearchMissingCommandEventArgs>(hstring{ missingCommand })); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 this goes up on the BG thread, then comes back down into us in |
||
| } | ||
|
|
||
| void ControlCore::_terminalClearQuickFix() | ||
| { | ||
| ClearQuickFix.raise(*this, nullptr); | ||
| } | ||
|
|
||
| bool ControlCore::HasSelection() const | ||
| { | ||
| const auto lock = _terminal->LockForReading(); | ||
|
|
@@ -2258,9 +2276,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| auto context = winrt::make_self<CommandHistoryContext>(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)? | ||
carlos-zamora marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // No clue which we should do. Thoughts? | ||
carlos-zamora marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| context->QuickFixes(_cachedQuickFixes); | ||
|
|
||
| return *context; | ||
| } | ||
|
|
||
| void ControlCore::UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is more of a |
||
| { | ||
| _cachedQuickFixes = quickFixes; | ||
| } | ||
|
|
||
| Core::Scheme ControlCore::ColorScheme() const noexcept | ||
| { | ||
| Core::Scheme s; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,6 +68,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| { | ||
| til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History; | ||
| til::property<winrt::hstring> CurrentCommandline; | ||
| til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> QuickFixes; | ||
carlos-zamora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| CommandHistoryContext(std::vector<winrt::hstring>&& history) | ||
| { | ||
|
|
@@ -242,6 +243,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
|
|
||
| hstring ReadEntireBuffer() const; | ||
| Control::CommandHistoryContext CommandHistory() const; | ||
| void UpdateQuickFixes(const Windows::Foundation::Collections::IVector<hstring>& quickFixes); | ||
|
|
||
| void AdjustOpacity(const float opacity, const bool relative); | ||
|
|
||
|
|
@@ -284,6 +286,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| til::typed_event<IInspectable, Control::UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers; | ||
| til::typed_event<IInspectable, Control::OpenHyperlinkEventArgs> OpenHyperlink; | ||
| til::typed_event<IInspectable, Control::CompletionsChangedEventArgs> CompletionsChanged; | ||
| til::typed_event<IInspectable, Control::SearchMissingCommandEventArgs> SearchMissingCommand; | ||
| til::typed_event<> ClearQuickFix; | ||
|
|
||
| til::typed_event<> CloseTerminalRequested; | ||
| til::typed_event<> RestartTerminalRequested; | ||
|
|
@@ -354,6 +358,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| til::point _contextMenuBufferPosition{ 0, 0 }; | ||
|
|
||
| Windows::Foundation::Collections::IVector<int32_t> _cachedSearchResultRows{ nullptr }; | ||
|
||
| Windows::Foundation::Collections::IVector<hstring> _cachedQuickFixes{ nullptr }; | ||
|
|
||
| void _setupDispatcherAndCallbacks(); | ||
|
|
||
|
|
@@ -378,6 +383,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation | |
| void _terminalPlayMidiNote(const int noteNumber, | ||
| const int velocity, | ||
| const std::chrono::microseconds duration); | ||
| void _terminalSearchMissingCommand(std::wstring_view missingCommand); | ||
| void _terminalClearQuickFix(); | ||
|
|
||
| winrt::fire_and_forget _terminalCompletionsChanged(std::wstring_view menuJson, unsigned int replaceLength); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.