Repository: microsoft/terminal (language: cpp)

Issue:
Recent commands UI
<!-- 
🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

I ACKNOWLEDGE THE FOLLOWING BEFORE PROCEEDING:
1. If I delete this entire template and go my own path, the core team may close my issue without further explanation or engagement.
2. If I list multiple bugs/concerns in this one issue, the core team may close my issue without further explanation or engagement.
3. If I write an issue that has many duplicates, the core team may close my issue without further explanation or engagement (and without necessarily spending time to find the exact duplicate ID number).
4. If I leave the title incomplete when filing the issue, the core team may close my issue without further explanation or engagement.
5. If I file something completely blank in the body, the core team may close my issue without further explanation or engagement.

All good? Then proceed!
-->

# Description of the new feature/enhancement

In Linux, almost every terminal stores a persistent history of commands. I think in this case it's the interpreter like `bash` or `zsh`. But I think it should be possible to do it on app level per user as well.

<!-- 
A clear and concise description of what the problem is that the new feature would solve.
Describe why and how a user would use this new functionality (if applicable).
-->

# Proposed technical implementation details (optional)

A simple SQLite DB file per user and per command interpreter (cmd, powershell, etc.) that stores the history of commands could be used. The location should be configurable in the preferences JSON.

<!-- 
A clear and concise description of what you want to happen.
-->

1. User enters:
    ```
    backup f: n:\backups\computername\system
    format f:
    ```

2. User closes terminal and maybe even reboots windows

3. User opens terminal back up and can push the arrow up key to recall the last commands they have executed.

4. User cannot find what they're searching for, so they type:
    ```
    history
    ```
    And sees a list like this:
    ```
    0 2022-02-03 14:33: backup f: n:\backups\computername\system
    1 2022-02-03 14:43: format f:
    ```
    And can redo the command using `!1` or `!0`.
    With `#0` the line pops up to be edited before executing with the `RETURN` key


Discussion / hints:

Fix patch:
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index cad581983f3..2b953dd24a2 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -1257,6 +1257,34 @@ namespace winrt::TerminalApp::implementation
         }
     }
 
+    void TerminalPage::_HandleSuggestions(const IInspectable& /*sender*/,
+                                          const ActionEventArgs& args)
+    {
+        if (args)
+        {
+            if (const auto& realArgs = args.ActionArgs().try_as<SuggestionsArgs>())
+            {
+                auto source = realArgs.Source();
+
+                switch (source)
+                {
+                case SuggestionsSource::CommandHistory:
+                {
+                    if (const auto& control{ _GetActiveControl() })
+                    {
+                        const auto context = control.CommandHistory();
+                        _OpenSuggestions(control,
+                                         Command::HistoryToCommands(context.History(), context.CurrentCommandline(), false),
+                                         SuggestionsMode::Palette);
+                    }
+                    args.Handled(true);
+                }
+                break;
+                }
+            }
+        }
+    }
+
     void TerminalPage::_HandleColorSelection(const IInspectable& /*sender*/,
                                              const ActionEventArgs& args)
     {
diff --git a/src/cascadia/TerminalApp/SuggestionsControl.cpp b/src/cascadia/TerminalApp/SuggestionsControl.cpp
index 83694267a6d..528fe9f4b71 100644
--- a/src/cascadia/TerminalApp/SuggestionsControl.cpp
+++ b/src/cascadia/TerminalApp/SuggestionsControl.cpp
@@ -96,17 +96,17 @@ namespace winrt::TerminalApp::implementation
             }
         });
 
-        // Focusing the ListView when the Command Palette control is set to Visible
-        // for the first time fails because the ListView hasn't finished loading by
-        // the time Focus is called. Luckily, We can listen to SizeChanged to know
-        // when the ListView has been measured out and is ready, and we'll immediately
-        // revoke the handler because we only needed to handle it once on initialization.
         _sizeChangedRevoker = _filteredActionsView().SizeChanged(winrt::auto_revoke, [this](auto /*s*/, auto /*e*/) {
-            // This does only fire once, when the size changes, which is the
-            // very first time it's opened. It does not fire for subsequent
-            // openings.
-
-            _sizeChangedRevoker.revoke();
+            // When we're in BottomUp mode, we need to adjust our own position
+            // so that our bottom is aligned with our origin. This will ensure
+            // that as the menu changes in size (as we filter results), the menu
+            // stays "attached" to the cursor.
+            if (Visibility() == Visibility::Visible && _direction == TerminalApp::SuggestionsDirection::BottomUp)
+            {
+                auto m = this->Margin();
+                m.Top = (_anchor.Y - ActualHeight());
+                this->Margin(m);
+            }
         });
 
         _filteredActionsView().SelectionChanged({ this, &SuggestionsControl::_selectedCommandChanged });
@@ -851,11 +851,13 @@ namespace winrt::TerminalApp::implementation
                 }
             }
         }
-        if (_mode == SuggestionsMode::Palette)
-        {
-            // We want to present the commands sorted
-            std::sort(actions.begin(), actions.end(), FilteredCommand::Compare);
-        }
+
+        // No sorting in palette mode, so results are still filtered, but in the
+        // original order. This feels more right for something like
+        // recentCommands.
+        //
+        // This is in contrast to the Command Palette, which always sorts its
+        // actions.
 
         // Adjust the order of the results depending on if we're top-down or
         // bottom up. This way, the "first" / "best" match is always closest to
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index c33d33d2cc5..eefbdd54856 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -1951,6 +1951,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         return hstring{ str };
     }
 
+    // Get all of our recent commands. This will only really work if the user has enabled shell integration.
+    Control::CommandHistoryContext ControlCore::CommandHistory() const
+    {
+        auto terminalLock = _terminal->LockForWriting();
+        const auto& textBuffer = _terminal->GetTextBuffer();
+
+        std::vector<winrt::hstring> commands;
+        for (const auto& mark : _terminal->GetScrollMarks())
+        {
+            // The command text is between the `end` (which denotes the end of
+            // the prompt) and the `commandEnd`.
+            bool markHasCommand = mark.commandEnd.has_value() &&
+                                  mark.commandEnd != mark.end;
+            if (!markHasCommand)
+            {
+                continue;
+            }
+
+            // Get the text of the command
+            const auto line = mark.end.y;
+            const auto& row = textBuffer.GetRowByOffset(line);
+            const auto commandText = row.GetText(mark.end.x, mark.commandEnd->x);
+
+            // Trim off trailing spaces.
+            const auto strEnd = commandText.find_last_not_of(UNICODE_SPACE);
+            if (strEnd != std::string::npos)
+            {
+                const auto trimmed = commandText.substr(0, strEnd + 1);
+                commands.push_back(winrt::hstring{ trimmed });
+            }
+        }
+        auto context = winrt::make_self<CommandHistoryContext>(std::move(commands));
+
+        return *context;
+    }
+
     Core::Scheme ControlCore::ColorScheme() const noexcept
     {
         Core::Scheme s;
diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h
index eea115952f9..5f9f23a3e89 100644
--- a/src/cascadia/TerminalControl/ControlCore.h
+++ b/src/cascadia/TerminalControl/ControlCore.h
@@ -17,6 +17,7 @@
 
 #include "ControlCore.g.h"
 #include "SelectionColor.g.h"
+#include "CommandHistoryContext.g.h"
 #include "ControlSettings.h"
 #include "../../audio/midi/MidiAudio.hpp"
 #include "../../renderer/base/Renderer.hpp"
@@ -53,6 +54,16 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         til::property<til::color> Color;
         til::property<bool> IsIndex16;
     };
+    struct CommandHistoryContext : CommandHistoryContextT<CommandHistoryContext>
+    {
+        til::property<Windows::Foundation::Collections::IVector<winrt::hstring>> History;
+        til::property<winrt::hstring> CurrentCommandline;
+
+        CommandHistoryContext(std::vector<winrt::hstring>&& history)
+        {
+            History(winrt::single_threaded_vector<winrt::hstring>(std::move(history)));
+        }
+    };
 
     struct ControlCore : ControlCoreT<ControlCore>
     {
@@ -213,6 +224,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         void SetReadOnlyMode(const bool readOnlyState);
 
         hstring ReadEntireBuffer() const;
+        Control::CommandHistoryContext CommandHistory() const;
 
         static bool IsVintageOpacityAvailable() noexcept;
 
diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl
index 8f8f87f2e4b..8157346dcd1 100644
--- a/src/cascadia/TerminalControl/ControlCore.idl
+++ b/src/cascadia/TerminalControl/ControlCore.idl
@@ -58,6 +58,12 @@ namespace Microsoft.Terminal.Control
         Boolean IsIndex16;
     };
 
+    [default_interface] runtimeclass CommandHistoryContext
+    {
+        IVector<String> History { get; };
+        String CurrentCommandline { get; };
+    };
+
     [default_interface] runtimeclass ControlCore : ICoreState
     {
         ControlCore(IControlSettings settings,
@@ -136,6 +142,7 @@ namespace Microsoft.Terminal.Control
         void EnablePainting();
 
         String ReadEntireBuffer();
+        CommandHistoryContext CommandHistory();
 
         void AdjustOpacity(Double Opacity, Boolean relative);
         void WindowVisibilityChanged(Boolean showOrHide);
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index c82de3b3acd..2bce8ecc0d0 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -3377,6 +3377,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
     {
         return _core.ReadEntireBuffer();
     }
+    Control::CommandHistoryContext TermControl::CommandHistory() const
+    {
+        return _core.CommandHistory();
+    }
 
     Core::Scheme TermControl::ColorScheme() const noexcept
     {
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index a11c3f7c469..6b05e7e9a83 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -142,6 +142,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         static Windows::UI::Xaml::Thickness ParseThicknessFromPadding(const hstring padding);
 
         hstring ReadEntireBuffer() const;
+        Control::CommandHistoryContext CommandHistory() const;
 
         winrt::Microsoft::Terminal::Core::Scheme ColorScheme() const noexcept;
         void ColorScheme(const winrt::Microsoft::Terminal::Core::Scheme& scheme) const noexcept;
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index 11b47d8de35..63495109325 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -112,6 +112,7 @@ namespace Microsoft.Terminal.Control
         void SetReadOnly(Boolean readOnlyState);
 
         String ReadEntireBuffer();
+        CommandHistoryContext CommandHistory();
 
         void AdjustOpacity(Double Opacity, Boolean relative);
 
diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
index 7c9802c07cb..ae81d5b498f 100644
--- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
@@ -52,6 +52,7 @@ static constexpr std::string_view SwitchToTabKey{ "switchToTab" };
 static constexpr std::string_view TabSearchKey{ "tabSearch" };
 static constexpr std::string_view ToggleAlwaysOnTopKey{ "toggleAlwaysOnTop" };
 static constexpr std::string_view ToggleCommandPaletteKey{ "commandPalette" };
+static constexpr std::string_view SuggestionsKey{ "showSuggestions" };
 static constexpr std::string_view ToggleFocusModeKey{ "toggleFocusMode" };
 static constexpr std::string_view SetFocusModeKey{ "setFocusMode" };
 static constexpr std::string_view ToggleFullscreenKey{ "toggleFullscreen" };
@@ -386,6 +387,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
                 { ShortcutAction::TabSearch, RS_(L"TabSearchCommandKey") },
                 { ShortcutAction::ToggleAlwaysOnTop, RS_(L"ToggleAlwaysOnTopCommandKey") },
                 { ShortcutAction::ToggleCommandPalette, MustGenerate },
+                { ShortcutAction::Suggestions, MustGenerate },
                 { ShortcutAction::ToggleFocusMode, RS_(L"ToggleFocusModeCommandKey") },
                 { ShortcutAction::SetFocusMode, MustGenerate },
                 { ShortcutAction::ToggleFullscreen, RS_(L"ToggleFullscreenCommandKey") },
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
index 64f7bd13b0c..6dc778df909 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.cpp
@@ -33,6 +33,7 @@
 #include "AddMarkArgs.g.cpp"
 #include "FindMatchArgs.g.cpp"
 #include "ToggleCommandPaletteArgs.g.cpp"
+#include "SuggestionsArgs.g.cpp"
 #include "NewWindowArgs.g.cpp"
 #include "PrevTabArgs.g.cpp"
 #include "NextTabArgs.g.cpp"
@@ -706,6 +707,16 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         return RS_(L"ToggleCommandPaletteCommandKey");
     }
 
+    winrt::hstring SuggestionsArgs::GenerateName() const
+    {
+        switch (Source())
+        {
+        case SuggestionsSource::CommandHistory:
+            return RS_(L"SuggestionsCommandHistoryCommandKey");
+        }
+        return RS_(L"SuggestionsCommandKey");
+    }
+
     winrt::hstring FindMatchArgs::GenerateName() const
     {
         switch (Direction())
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h
index 99f52ad991d..8e278e16a3a 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.h
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h
@@ -34,6 +34,7 @@
 #include "AddMarkArgs.g.h"
 #include "MoveTabArgs.g.h"
 #include "ToggleCommandPaletteArgs.g.h"
+#include "SuggestionsArgs.g.h"
 #include "FindMatchArgs.g.h"
 #include "NewWindowArgs.g.h"
 #include "PrevTabArgs.g.h"
@@ -213,6 +214,10 @@ private:                                                                    \
 #define TOGGLE_COMMAND_PALETTE_ARGS(X) \
     X(CommandPaletteLaunchMode, LaunchMode, "launchMode", false, CommandPaletteLaunchMode::Action)
 
+////////////////////////////////////////////////////////////////////////////////
+#define SUGGESTIONS_ARGS(X) \
+    X(SuggestionsSource, Source, "source", false, SuggestionsSource::Tasks)
+
 ////////////////////////////////////////////////////////////////////////////////
 #define FIND_MATCH_ARGS(X) \
     X(FindMatchDirection, Direction, "direction", args->Direction() == FindMatchDirection::None, FindMatchDirection::None)
@@ -709,6 +714,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
     ACTION_ARGS_STRUCT(ToggleCommandPaletteArgs, TOGGLE_COMMAND_PALETTE_ARGS);
 
+    ACTION_ARGS_STRUCT(SuggestionsArgs, SUGGESTIONS_ARGS);
+
     ACTION_ARGS_STRUCT(FindMatchArgs, FIND_MATCH_ARGS);
 
     ACTION_ARGS_STRUCT(PrevTabArgs, PREV_TAB_ARGS);
@@ -836,6 +843,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
     BASIC_FACTORY(ClearBufferArgs);
     BASIC_FACTORY(MultipleActionsArgs);
     BASIC_FACTORY(AdjustOpacityArgs);
+    BASIC_FACTORY(SuggestionsArgs);
     BASIC_FACTORY(SelectCommandArgs);
     BASIC_FACTORY(SelectOutputArgs);
 }
diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
index a5daa935ece..b061eaae2fe 100644
--- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl
+++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl
@@ -112,6 +112,12 @@ namespace Microsoft.Terminal.Settings.Model
         ToCurrent,
         ToMouse,
     };
+    enum SuggestionsSource
+    {
+        Tasks,
+        CommandHistory,
+        DirectoryHistory,
+    };
 
     [default_interface] runtimeclass NewTerminalArgs {
         NewTerminalArgs();
@@ -318,6 +324,13 @@ namespace Microsoft.Terminal.Settings.Model
         CommandPaletteLaunchMode LaunchMode { get; };
     };
 
+    [default_interface] runtimeclass SuggestionsArgs : IActionArgs
+    {
+        SuggestionsArgs();
+        SuggestionsArgs(SuggestionsSource source);
+        SuggestionsSource Source { get; };
+    };
+
     [default_interface] runtimeclass FindMatchArgs : IActionArgs
     {
         FindMatchArgs(FindMatchDirection direction);
diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
index 53a260f46ea..df008b58220 100644
--- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
+++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
@@ -103,6 +103,7 @@
     ON_ALL_ACTIONS(MarkMode)                \
     ON_ALL_ACTIONS(ToggleBlockSelection)    \
     ON_ALL_ACTIONS(SwitchSelectionEndpoint) \
+    ON_ALL_ACTIONS(Suggestions)             \
     ON_ALL_ACTIONS(ColorSelection)          \
     ON_ALL_ACTIONS(ShowContextMenu)         \
     ON_ALL_ACTIONS(ExpandSelectionToWord)   \
@@ -150,6 +151,7 @@
     ON_ALL_ACTIONS_WITH_ARGS(ClearBuffer)          \
     ON_ALL_ACTIONS_WITH_ARGS(MultipleActions)      \
     ON_ALL_ACTIONS_WITH_ARGS(AdjustOpacity)        \
+    ON_ALL_ACTIONS_WITH_ARGS(Suggestions)          \
     ON_ALL_ACTIONS_WITH_ARGS(SelectCommand)        \
     ON_ALL_ACTIONS_WITH_ARGS(SelectOutput)         \
     ON_ALL_ACTIONS_WITH_ARGS(ColorSelection)
diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp
index e2eec5c95b0..b34e5a02c2e 100644
--- a/src/cascadia/TerminalSettingsModel/Command.cpp
+++ b/src/cascadia/TerminalSettingsModel/Command.cpp
@@ -730,4 +730,53 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
 
         return winrt::single_threaded_vector<Model::Command>(std::move(result));
     }
+
+    // Method description:
+    // * Convert the list of recent commands into a list of sendInput actions to
+    //   send those commands.
+    // * We'll give each command a "history" icon.
+    // * If directories is true, we'll prepend "cd " to each command, so that
+    //   the command will be run as a directory change instead.
+    IVector<Model::Command> Command::HistoryToCommands(IVector<winrt::hstring> history,
+                                                       winrt::hstring /*currentCommandline*/,
+                                                       bool directories)
+    {
+        std::wstring cdText = directories ? L"cd " : L"";
+        auto result = std::vector<Model::Command>();
+
+        // Use this map to discard duplicates.
+        std::unordered_map<std::wstring_view, bool> foundCommands{};
+
+        auto backspaces = std::wstring(::base::saturated_cast<size_t>(0), L'\x7f');
+
+        // Iterate in reverse over the history, so that most recent commands are first
+        for (auto i = history.Size(); i > 0; i--)
+        {
+            std::wstring_view line{ history.GetAt(i - 1) };
+
+            if (line.empty())
+            {
+                continue;
+            }
+            if (foundCommands.contains(line))
+            {
+                continue;
+            }
+            auto args = winrt::make_self<SendInputArgs>(
+                winrt::hstring{ fmt::format(L"{}{}{}", cdText, backspaces, line) });
+
+            Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, *args };
+
+            auto command = winrt::make_self<Command>();
+            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
+            result.push_back(*command);
+            foundCommands[line] = true;
+        }
+
+        return winrt::single_threaded_vector<Model::Command>(std::move(result));
+    }
 }
diff --git a/src/cascadia/TerminalSettingsModel/Command.h b/src/cascadia/TerminalSettingsModel/Command.h
index b001c795671..59a080c4c0a 100644
--- a/src/cascadia/TerminalSettingsModel/Command.h
+++ b/src/cascadia/TerminalSettingsModel/Command.h
@@ -67,6 +67,9 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
         void IconPath(const hstring& val);
 
         static Windows::Foundation::Collections::IVector<Model::Command> ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength);
+        static Windows::Foundation::Collections::IVector<Model::Command> HistoryToCommands(Windows::Foundation::Collections::IVector<winrt::hstring> history,
+                                                                                           winrt::hstring currentCommandline,
+                                                                                           bool directories);
 
         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 934df64d6cb..e92f459e69d 100644
--- a/src/cascadia/TerminalSettingsModel/Command.idl
+++ b/src/cascadia/TerminalSettingsModel/Command.idl
@@ -47,5 +47,7 @@ namespace Microsoft.Terminal.Settings.Model
         Windows.Foundation.Collections.IMapView<String, Command> NestedCommands { get; };
 
         static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
+        static IVector<Command> HistoryToCommands(IVector<String> commandHistory, String commandline, Boolean directories);
+
     }
 }
diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
index 8de5d841734..74035df755d 100644
--- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
@@ -449,6 +449,12 @@
   <data name="ToggleCommandPaletteCommandKey" xml:space="preserve">
     <value>Toggle command palette</value>
   </data>
+  <data name="SuggestionsCommandHistoryCommandKey" xml:space="preserve">
+    <value>Recent commands...</value>
+  </data>
+  <data name="SuggestionsCommandKey" xml:space="preserve">
+    <value>Open suggestions...</value>
+  </data>
   <data name="ToggleCommandPaletteCommandLineModeCommandKey" xml:space="preserve">
     <value>Toggle command palette in command line mode</value>
   </data>
@@ -714,4 +720,4 @@
     <value>Search the web for selected text</value>
     <comment>This will open a web browser to search for some user-selected text</comment>
   </data>
-</root>
+</root>
\ No newline at end of file
diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
index 4685f6f3581..73af3411e2d 100644
--- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
+++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h
@@ -499,6 +499,15 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::FindMatchDirecti
     };
 };
 
+JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::SuggestionsSource)
+{
+    JSON_MAPPINGS(3) = {
+        pair_type{ "tasks", ValueType::Tasks },
+        pair_type{ "commandHistory", ValueType::CommandHistory },
+        pair_type{ "directoryHistory", ValueType::DirectoryHistory }
+    };
+};
+
 JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::WindowingMode)
 {
     JSON_MAPPINGS(3) = {


Test patch: