Repository: microsoft/terminal (language: cpp)

Issue:
Add a key binding to open Quick Fix
Dependent on #16848 


Discussion / hints:

Fix patch:
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index ac01af8cb3b..88794eda57c 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -1461,7 +1461,19 @@ namespace winrt::TerminalApp::implementation
                 }
 
                 // Aggregate all the commands from the different sources that
-                // the user selected.
+                // the user selected. This is the order presented to the user
+
+                if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) &&
+                    context != nullptr &&
+                    context.QuickFixes() != nullptr)
+                {
+                    // \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);
+                    }
+                }
 
                 // Tasks are all the sendInput commands the user has saved in
                 // their settings file. Ask the ActionMap for those.
@@ -1488,18 +1500,6 @@ namespace winrt::TerminalApp::implementation
                     }
                 }
 
-                if (WI_IsFlagSet(source, SuggestionsSource::QuickFixes) &&
-                    context != nullptr &&
-                    context.QuickFixes() != nullptr)
-                {
-                    // \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);
-                    }
-                }
-
                 // Open the palette with all these commands in it.
                 _OpenSuggestions(_GetActiveControl(),
                                  winrt::single_threaded_vector<Command>(std::move(commandsCollection)),
@@ -1593,4 +1593,14 @@ namespace winrt::TerminalApp::implementation
         _ShowAboutDialog();
         args.Handled(true);
     }
+
+    void TerminalPage::_HandleQuickFix(const IInspectable& /*sender*/,
+                                       const ActionEventArgs& args)
+    {
+        if (const auto& control{ _GetActiveControl() })
+        {
+            const auto handled = control.OpenQuickFixMenu();
+            args.Handled(handled);
+        }
+    }
 }
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index 6bc3d488b5b..e53a8ada1c4 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -340,6 +340,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
                 }
             }
         });
+        if constexpr (Feature_QuickFix::IsEnabled())
+        {
+            QuickFixMenu().Closed([weakThis = get_weak()](auto&&, auto&&) {
+                if (auto control{ weakThis.get() }; control && !control->_IsClosing())
+                {
+                    // Expand the quick fix button if it's collapsed (looks nicer)
+                    if (control->_quickFixButtonCollapsible)
+                    {
+                        VisualStateManager::GoToState(*control, StateCollapsed, false);
+                    }
+                }
+            });
+        }
     }
 
     void TermControl::_QuickFixButton_PointerEntered(const IInspectable& /*sender*/, const PointerRoutedEventArgs& /*e*/)
@@ -3555,8 +3568,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
 
         if (Feature_QuickFix::IsEnabled())
         {
-            auto quickFixBtn = FindName(L"QuickFixButton").as<Controls::Button>();
-            quickFixBtn.Height(args.Height() / dpiScale);
+            QuickFixButton().Height(args.Height() / dpiScale);
             QuickFixIcon().FontSize(static_cast<double>(args.Width() / dpiScale));
             RefreshQuickFixMenu();
         }
@@ -3892,6 +3904,25 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         return std::max(CharacterDimensions().Width * 2.0 / 3.0, GetPadding().Left);
     }
 
+    bool TermControl::OpenQuickFixMenu()
+    {
+        if constexpr (Feature_QuickFix::IsEnabled())
+        {
+            if (_core.QuickFixesAvailable())
+            {
+                // Expand the quick fix button if it's collapsed (looks nicer)
+                if (_quickFixButtonCollapsible)
+                {
+                    VisualStateManager::GoToState(*this, StateNormal, false);
+                }
+                auto quickFixBtn = QuickFixButton();
+                quickFixBtn.Flyout().ShowAt(quickFixBtn);
+                return true;
+            }
+        }
+        return false;
+    }
+
     void TermControl::RefreshQuickFixMenu()
     {
         if (!Feature_QuickFix::IsEnabled())
@@ -3899,7 +3930,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             return;
         }
 
-        auto quickFixBtn = FindName(L"QuickFixButton").as<Controls::Button>();
+        auto quickFixBtn = QuickFixButton();
         if (!_core.QuickFixesAvailable())
         {
             quickFixBtn.Visibility(Visibility::Collapsed);
@@ -3920,7 +3951,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         const auto viewportBufferPosition = rd->GetViewport();
         const auto cursorBufferPosition = rd->GetCursorPosition();
         rd->UnlockConsole();
-        if (cursorBufferPosition.y < viewportBufferPosition.Top() || cursorBufferPosition.y > viewportBufferPosition.BottomExclusive())
+        if (cursorBufferPosition.y < viewportBufferPosition.Top() || cursorBufferPosition.y > viewportBufferPosition.BottomInclusive())
         {
             quickFixBtn.Visibility(Visibility::Collapsed);
             return;
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index 241eb1d6c7a..13166e18c7a 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -182,6 +182,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         void RawWriteString(const winrt::hstring& text);
 
         void ShowContextMenu();
+        bool OpenQuickFixMenu();
         void RefreshQuickFixMenu();
         void ClearQuickFix();
 
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index bc003c3e667..7b643ad1cb3 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -149,6 +149,7 @@ namespace Microsoft.Terminal.Control
         Double QuickFixButtonCollapsedWidth { get; };
 
         void ShowContextMenu();
+        Boolean OpenQuickFixMenu();
         void RefreshQuickFixMenu();
         void ClearQuickFix();
 
diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp
index c6301dd1eeb..01987a3bd5e 100644
--- a/src/cascadia/TerminalCore/Terminal.cpp
+++ b/src/cascadia/TerminalCore/Terminal.cpp
@@ -1597,12 +1597,13 @@ void Terminal::ColorSelection(const TextAttribute& attr, winrt::Microsoft::Termi
 }
 
 // Method Description:
-// - Returns the position of the cursor relative to the active viewport
+// - Returns the position of the cursor relative to the visible viewport
 til::point Terminal::GetViewportRelativeCursorPosition() const noexcept
 {
     const auto absoluteCursorPosition{ GetCursorPosition() };
-    const auto viewport{ _GetMutableViewport() };
-    return absoluteCursorPosition - viewport.Origin();
+    const auto mutableViewport{ _GetMutableViewport() };
+    const auto relativeCursorPos = absoluteCursorPosition - mutableViewport.Origin();
+    return { relativeCursorPos.x, relativeCursorPos.y + _scrollOffset };
 }
 
 void Terminal::PreviewText(std::wstring_view input)
diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
index 8fe015b8b15..e4f0205a2dd 100644
--- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
@@ -98,6 +98,7 @@ static constexpr std::string_view RestartConnectionKey{ "restartConnection" };
 static constexpr std::string_view ToggleBroadcastInputKey{ "toggleBroadcastInput" };
 static constexpr std::string_view OpenScratchpadKey{ "experimental.openScratchpad" };
 static constexpr std::string_view OpenAboutKey{ "openAbout" };
+static constexpr std::string_view QuickFixKey{ "quickFix" };
 
 static constexpr std::string_view ActionKey{ "action" };
 
@@ -438,6 +439,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
                 { ShortcutAction::ToggleBroadcastInput, RS_(L"ToggleBroadcastInputCommandKey") },
                 { ShortcutAction::OpenScratchpad, RS_(L"OpenScratchpadKey") },
                 { ShortcutAction::OpenAbout, RS_(L"OpenAboutCommandKey") },
+                { ShortcutAction::QuickFix, RS_(L"QuickFixCommandKey") },
             };
         }();
 
diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
index 2432b40e12c..daeb1f399bd 100644
--- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
+++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
@@ -111,7 +111,8 @@
     ON_ALL_ACTIONS(RestartConnection)       \
     ON_ALL_ACTIONS(ToggleBroadcastInput)    \
     ON_ALL_ACTIONS(OpenScratchpad)          \
-    ON_ALL_ACTIONS(OpenAbout)
+    ON_ALL_ACTIONS(OpenAbout)               \
+    ON_ALL_ACTIONS(QuickFix)
 
 #define ALL_SHORTCUT_ACTIONS_WITH_ARGS             \
     ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize)       \
diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
index f4b39abc297..33ee2ef2942 100644
--- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
@@ -734,4 +734,7 @@
   <data name="SaveSnippetNamePrefix" xml:space="preserve">
     <value>Save Snippet</value>
   </data>
+  <data name="QuickFixCommandKey" xml:space="preserve">
+    <value>Open quick fix menu</value>
+  </data>
 </root>
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index f6713e265b0..1d2b8f2c472 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -447,6 +447,8 @@
         { "command": "restoreLastClosed", "id": "Terminal.RestoreLastClosed" },
         { "command": "openAbout", "id": "Terminal.OpenAboutDialog" },
         { "command": "experimental.openTasks", "id": "Terminal.OpenTasks" },
+        { "command": "quickFix", "id": "Terminal.QuickFix" },
+        { "command": { "action": "showSuggestions", "source": "all"}, "id": "Terminal.Suggestions" },
 
         // Tab Management
         // "command": "closeTab" is unbound by default.
@@ -639,6 +641,7 @@
         { "keys":"ctrl+shift+p", "id": "Terminal.ToggleCommandPalette" },
         { "keys":"win+sc(41)", "id": "Terminal.QuakeMode" },
         { "keys": "alt+space", "id": "Terminal.OpenSystemMenu" },
+        { "keys": "ctrl+shift+period", "id": "Terminal.Suggestions" },
 
         // Tab Management
         // "command": "closeTab" is unbound by default.


Test patch: