Repository: microsoft/terminal (language: cpp)

Issue:
Terminal: Support keyboard text selection & navigation
Hello,

I'm using tmux on Linux machines and ConEmu on Windows machines and I really love that I can do text selection with the keyboard.

In ConEmu, there is a keybinding for "Start text selection (like text editors)", whereas in tmux there is the same thing with `<prefix><b>` (I think that is default), where I get a cursor that I can move with the arrow keys around the console window.
While doing this, I can select text while holding shift and (in case of ConEmu) press ENTER to copy the text into the clipboard and end the text selection.
It can also be compared with Vim's visual mode.

It would be nice if the new Windows Terminal could support this out of the box, so I can use it in e.g. PowerShell :-)


Discussion / hints:
@carlos-zamora this may be of interest
Also, if the tmux property _mouse-mode_ does not work in the new Terminal.
It works well in conhost.
One possible implementation would be just to do what cmd does, where if the user has selected text and presses "Enter", it will just copy the selected text to the clipboard.
Or maybe it would be possible to let the application running control what's happening when marking...
Or that the user can change between the "cmd mode" and other things
If keyboard text selection is added, please don't forget about things like Shift+Home and Shift+End.

You could also have a selection go up and down a line/row with Shift+Up and Shift+Down.

Oh and with all these ligatures, copying and pasting text should probably uncombine those ligature characters - so the font choice does not affect what may get pasted in another place.
Added the "Help-Wanted" label. Most of this work should be able to go in `TermControl` (probably `KeyDownHandler()`). If the selection is active, edit the `Terminal`s `_endSelectionPositionAnchor` by using `_terminal->SetEndSelectionPosition(terminalPosition);`

`Terminal`s `_ExpandWideGlyphSelectionLeft()` and `_ExpandWideGlyphSelectionRight()` can be used to expand the selection on wide glyphs appropriately. We'd just have to make them public or have something else call them.
Terminal is slowly driving me crazy because unlike ConEmu pressing <enter> clears the selection without copying it to the clipboard, having full-on keyboard only text selection would be a massive addition.
One fun wrinkle--PSReadLine handles buffer selection and navigation for Windows PowerShell and PSCore. So if this does get implemented, there should also be an option to disable it per-profile, so a shell can handle selection itself.
@pingzing I'm also using PSReadLine, but I'm unaware of the feature. Can you give me a hint? (can't find any documentation on this).

What I'm talking about in this issue is the following (beware awesome screencapturing):

![4urdJqAMlL](https://user-images.githubusercontent.com/2401875/62821852-babf9500-bb7b-11e9-9a83-a599a5b79384.gif)


I think it only works for the input buffer, and not previously-written text. No idea if it's documented, but it's definitely in the keybindings. For example, invoking `Get-PSReadLineKeyHandler` yields the following for me:

```
Selection functions
===================
Key                   Function            Description
---                   --------            -----------
Ctrl+a                SelectAll           Select the entire line. Moves the cursor to the end of the line
Shift+LeftArrow       SelectBackwardChar  Adjust the current selection to include the previous character
Shift+Home            SelectBackwardsLine Adjust the current selection to include from the cursor to the end of the line
Shift+Ctrl+LeftArrow  SelectBackwardWord  Adjust the current selection to include the previous word
Shift+RightArrow      SelectForwardChar   Adjust the current selection to include the next character
Shift+End             SelectLine          Adjust the current selection to include from the cursor to the start of the line
Shift+Ctrl+RightArrow SelectNextWord      Adjust the current selection to include the next word
```

My fear is that if Terminal implements this, it'll interfere with PSReadLine's own handling. ConEmu has similar behavior, which I have to disable if I want PSReadLine's behavior.
> One possible implementation would be just to do what cmd does, where if the user has selected text and presses "Enter", it will just copy the selected text to the clipboard.

I completely agree. I'm hitting this issue several times a day with Windows Terminal. After using cmd and ConEmu for years, I have been always using copy from text selection by pressing "ENTER", but the fact that Windows Terminal doesn't replicate that existing behavior is quite a burden so far. I'm constantly loosing my selection by pressing enter while I should not but use right mouse click. I really love Windows Terminal, but the missing legacy behavior of "ENTER" for copy is really hurting my productivity (I'm probably also starting to feel old that my brain is not able to quickly adapt to the new mouse right click, but that's a different story! 😅)

Is there any implementation problems to allow "ENTER" on copy - even if it is not the default but we can turn it on via config?
I agree with @xoofx on all.
Also asked-for here: https://twitter.com/Larswa/status/1203458135245107206 
> Is there any implementation problems to allow "ENTER" on copy - even if it is not the default but we can turn it on via config?

If you're going to support it, please do not add **_only_** copy on ENTER.  I cannot stand that. BUT, since others swear by it, and everyone either _loves_ or _hates_ each possibility, there is only one solution:  **Leave it to the user!**

Given that you have already enabled a way to control copy/paste, I assume I don't need to say this but because it will make a difference in whether people continue to use and even love Windows Terminal.

**Bonus points for also supporting mouse buttons** (e.g. middle and custom specialty buttons) with or without modifier keys (shift+button to copy, button to paste).  Again, unlock the possibilities and leave it to the user to customize.  <3
It would be great to have text selection movement configurable. For example if someone want to configure it as in VIM's visual mode ('w' for word forward, 'b' for backsword ...) 
We're not planning on adding <kbd>enter</kbd> as copy by default. The user is absolutely free to add that keybinding if they'd like though.

Configuring what the mouse buttons do is currently tracked by #1553.
I see @cinnamon-msft set this for v2. Any expectations on when we can see v2 even as beta? A rough estimate would be fine.
![image](https://user-images.githubusercontent.com/34687786/95051877-46847100-06ee-11eb-8da7-f2bf6494a2cb.png)
> Due by May 31, 2021
@mahmoudajawad
@mahmoudajawad We basically release features as they're developed. When we say this is in the 2.0 milestone, that just means we want to get to it before 2.0 ships, sometime next year. When there's a more concrete timetable, we'll update the milestone accordingly (e.g. if it's going to land in the 1.6 release, we'll move it to that milestone). 

I'm used to press "Shift + Arrow" (+Ctrl to jump words or Home, End, PgUp, PgDn) keys, what allow me to instantly select any text in terminal window and "Ctrl+C/Ctrl+V" to copy/paste in a very simple and fast manner (like in any text editor). 

That behavior is available in ConEmu. And it's common keyboard shortcuts behavior in all [CUA](https://en.wikipedia.org/wiki/IBM_Common_User_Access) appliactions what is majority in Windows, Linux, MacOS (basicaly almost every [GUI](https://en.wikipedia.org/wiki/Graphical_user_interface) app). 

It would be great to use same hotkeys for all apps on ones pc - less overhead about keyboard shortcuts more concentration on work. It would be even greater to have this in Windows Terminal.
Shift + Arrow / Home / End is working in Powershell but not for WSL. When I hit Shift + Home / End in WSL this sequence is pasted: [1;2H and [1;2F respectively.

Is there a way to configure WSL to work the same as Powershell?
@sinapis I'm pretty sure that's just how `bash` works. PowerShell manually handles the <kbd>shift+home</kbd> keys to highlight text, while `bash` doesn't know how to handle then, and just echos part of the keys to the input line. From some [simple searching](https://www.google.com/search?client=firefox-b-1-d&q=bash+select+text+with+keyboard), it looks like this is a tricky issue with linux shells: [here's one post](https://askubuntu.com/questions/302263/selecting-text-in-the-terminal-without-using-the-mouse), [here's another](https://stackoverflow.com/questions/1536757/selecting-text-in-terminal-without-using-the-mouse). 

Frankly, both of those don't seem to list any solutions I'd be happy with, so I think your best bet would be to wait for this to land, so the Terminal can take care of this for you 😉 
Has this been implemented yet?
@ofek Nope. This issue is still open, the spec (#2840) is still in review, and the PR (#3758) is still open. We'll make sure to update this thread when there is. In the meantime, might I recommend the Subscribe button?
![image](https://user-images.githubusercontent.com/18356694/91237459-5cbb0c80-e700-11ea-9347-b9b1ec2813b1.png)
That way you'll be notified of any updates to this thread, without needlessly pinging everyone on this thread ☺️
So what's the progress on this after 3 months?
As you can see, we haven't posted any updates since the last time someone asked. The team has been busy on other tasks (specifically, the settings UI). When there are updates, there will be comments made here, or on the spec in #2840, or in the PR in #3758.
> 
> 
> As you can see, we haven't posted any updates since the last time someone asked. The team has been busy on other tasks (specifically, the settings UI). When there are updates, there will be comments made here, or on the spec in #2840, or in the PR in #3758.

You have really weird priorities. This feature should have been there from the very start. It's even more infuriating because there is some bug that makes mouse text selection extremely slow/laggy after Windows Terminal has been running a few days..
![image](https://user-images.githubusercontent.com/18356694/113185000-80f14580-921b-11eb-86c0-6a5cffe6f1c3.png) vs ![image](https://user-images.githubusercontent.com/18356694/113185037-8e0e3480-921b-11eb-9d73-4d744881aebb.png)

I serve at the pleasure of the community, and the community feels that the settings UI is a higher priority. 

<hr>

That being said, we had a team discussion _literally two days ago_ about resurrecting the simple selection part of this (without finalizing the "mark mode" bits) sooner than later. Both are unanimous ship-blockers for 2.0, if that's any consolation.

When is v2.0 expected?
> When is v2.0 expected?

31-12-2021

https://github.com/microsoft/terminal/blob/main/doc/terminal-v2-roadmap.md
> > When is v2.0 expected?
> 
> 31-12-2021
> 
> https://github.com/microsoft/terminal/blob/main/doc/terminal-v2-roadmap.md

When can we expect it to rollout as a part of Windows 11?
> When can we expect it to rollout as a part of Windows 11?

The version of Terminal included in Windows 11 will automatically receive updates through the store; you do not need to wait for an OS update to get it.

If you're on Windows 10, you can just install it from the store.
Has this been implemented/released in a preview build?

Fix patch:
diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json
index 9de676a965a..17ab1f774cd 100644
--- a/doc/cascadia/profiles.schema.json
+++ b/doc/cascadia/profiles.schema.json
@@ -318,6 +318,7 @@
         "moveFocus",
         "movePane",
         "swapPane",
+        "markMode",
         "moveTab",
         "multipleActions",
         "newTab",
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index 3d61450e8c5..0cc395287a3 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -1043,4 +1043,14 @@ namespace winrt::TerminalApp::implementation
             args.Handled(true);
         }
     }
+
+    void TerminalPage::_HandleMarkMode(const IInspectable& /*sender*/,
+                                       const ActionEventArgs& args)
+    {
+        if (const auto& control{ _GetActiveControl() })
+        {
+            control.ToggleMarkMode();
+            args.Handled(true);
+        }
+    }
 }
diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp
index be5e7a70c1a..d24dca93cef 100644
--- a/src/cascadia/TerminalControl/ControlCore.cpp
+++ b/src/cascadia/TerminalControl/ControlCore.cpp
@@ -392,11 +392,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
             vkey != VK_SNAPSHOT &&
             keyDown)
         {
+            if (_terminal->IsInMarkMode() && modifiers.IsCtrlPressed() && vkey == 'A')
+            {
+                auto lock = _terminal->LockForWriting();
+                _terminal->SelectAll();
+                _renderer->TriggerSelection();
+                return true;
+            }
+
             // try to update the selection
-            if (const auto updateSlnParams{ ::Terminal::ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
+            if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
             {
                 auto lock = _terminal->LockForWriting();
-                _terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second);
+                _terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
                 _renderer->TriggerSelection();
                 return true;
             }
@@ -1001,6 +1009,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         _renderer->TriggerSelection();
     }
 
+    void ControlCore::ToggleMarkMode()
+    {
+        auto lock = _terminal->LockForWriting();
+        _terminal->ToggleMarkMode();
+        _renderer->TriggerSelection();
+    }
+
+    bool ControlCore::IsInMarkMode() const
+    {
+        return _terminal->IsInMarkMode();
+    }
+
     // Method Description:
     // - Pre-process text pasted (presumably from the clipboard)
     //   before sending it over the terminal's connection.
diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h
index 1a8b744cb7f..c2c0ef4f3d9 100644
--- a/src/cascadia/TerminalControl/ControlCore.h
+++ b/src/cascadia/TerminalControl/ControlCore.h
@@ -81,6 +81,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         void PasteText(const winrt::hstring& hstr);
         bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
         void SelectAll();
+        void ToggleMarkMode();
+        bool IsInMarkMode() const;
 
         void GotFocus();
         void LostFocus();
diff --git a/src/cascadia/TerminalControl/ControlCore.idl b/src/cascadia/TerminalControl/ControlCore.idl
index 55b718d1fd1..edcb9fb69b1 100644
--- a/src/cascadia/TerminalControl/ControlCore.idl
+++ b/src/cascadia/TerminalControl/ControlCore.idl
@@ -66,7 +66,9 @@ namespace Microsoft.Terminal.Control
         void SendInput(String text);
         void PasteText(String text);
         void SelectAll();
+        void ToggleMarkMode();
         void ClearBuffer(ClearBufferType clearType);
+        Boolean IsInMarkMode();
 
         void SetHoveredCell(Microsoft.Terminal.Core.Point terminalPosition);
         void ClearHoveredCell();
diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp
index f90a2a96bd1..786e5c39e3e 100644
--- a/src/cascadia/TerminalControl/TermControl.cpp
+++ b/src/cascadia/TerminalControl/TermControl.cpp
@@ -1123,7 +1123,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         {
             // Manually show the cursor when a key is pressed. Restarting
             // the timer prevents flickering.
-            _core.CursorOn(true);
+            _core.CursorOn(!_core.IsInMarkMode());
             _cursorTimer->Start();
         }
 
@@ -1594,7 +1594,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         if (_cursorTimer)
         {
             // When the terminal focuses, show the cursor immediately
-            _core.CursorOn(true);
+            _core.CursorOn(!_core.IsInMarkMode());
             _cursorTimer->Start();
         }
 
@@ -1845,6 +1845,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         _core.SelectAll();
     }
 
+    void TermControl::ToggleMarkMode()
+    {
+        _core.ToggleMarkMode();
+    }
+
     void TermControl::Close()
     {
         if (!_IsClosing())
diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h
index c76c4b58703..87d336c4843 100644
--- a/src/cascadia/TerminalControl/TermControl.h
+++ b/src/cascadia/TerminalControl/TermControl.h
@@ -38,6 +38,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
         bool CopySelectionToClipboard(bool singleLine, const Windows::Foundation::IReference<CopyFormat>& formats);
         void PasteTextFromClipboard();
         void SelectAll();
+        void ToggleMarkMode();
         void Close();
         Windows::Foundation::Size CharacterDimensions() const;
         Windows::Foundation::Size MinimumSize();
diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl
index b235962a73f..d11cbea9cfa 100644
--- a/src/cascadia/TerminalControl/TermControl.idl
+++ b/src/cascadia/TerminalControl/TermControl.idl
@@ -51,6 +51,7 @@ namespace Microsoft.Terminal.Control
         Boolean CopySelectionToClipboard(Boolean singleLine, Windows.Foundation.IReference<CopyFormat> formats);
         void PasteTextFromClipboard();
         void SelectAll();
+        void ToggleMarkMode();
         void ClearBuffer(ClearBufferType clearType);
         void Close();
         Windows.Foundation.Size CharacterDimensions { get; };
diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp
index 648d7fff902..11f6f664362 100644
--- a/src/cascadia/TerminalCore/Terminal.cpp
+++ b/src/cascadia/TerminalCore/Terminal.cpp
@@ -45,6 +45,7 @@ Terminal::Terminal() :
     _snapOnInput{ true },
     _altGrAliasing{ true },
     _blockSelection{ false },
+    _markMode{ false },
     _selection{ std::nullopt },
     _taskbarState{ 0 },
     _taskbarProgress{ 0 },
@@ -1328,7 +1329,7 @@ void Terminal::SetCursorOn(const bool isOn)
 bool Terminal::IsCursorBlinkingAllowed() const noexcept
 {
     const auto& cursor = _activeBuffer().GetCursor();
-    return cursor.IsBlinkingAllowed();
+    return !_markMode && cursor.IsBlinkingAllowed();
 }
 
 // Method Description:
diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp
index e89e9167b47..1a62d031a25 100644
--- a/src/cascadia/TerminalCore/Terminal.hpp
+++ b/src/cascadia/TerminalCore/Terminal.hpp
@@ -239,11 +239,13 @@ class Microsoft::Terminal::Core::Terminal final :
     void SetSelectionAnchor(const COORD position);
     void SetSelectionEnd(const COORD position, std::optional<SelectionExpansion> newExpansionMode = std::nullopt);
     void SetBlockSelection(const bool isEnabled) noexcept;
-    void UpdateSelection(SelectionDirection direction, SelectionExpansion mode);
+    void UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods);
     void SelectAll();
+    bool IsInMarkMode() const;
+    void ToggleMarkMode();
 
     using UpdateSelectionParams = std::optional<std::pair<SelectionDirection, SelectionExpansion>>;
-    static UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey);
+    UpdateSelectionParams ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const;
 
     const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace);
 #pragma endregion
@@ -313,6 +315,7 @@ class Microsoft::Terminal::Core::Terminal final :
     bool _blockSelection;
     std::wstring _wordDelimiters;
     SelectionExpansion _multiClickSelectionMode;
+    bool _markMode;
 #pragma endregion
 
     std::unique_ptr<TextBuffer> _mainBuffer;
diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp
index b730836f114..6d75d6d20c5 100644
--- a/src/cascadia/TerminalCore/TerminalSelection.cpp
+++ b/src/cascadia/TerminalCore/TerminalSelection.cpp
@@ -235,13 +235,40 @@ void Terminal::SetBlockSelection(const bool isEnabled) noexcept
     _blockSelection = isEnabled;
 }
 
-Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey)
+bool Terminal::IsInMarkMode() const
 {
-    if (mods.IsShiftPressed() && !mods.IsAltPressed())
+    return _markMode;
+}
+
+void Terminal::ToggleMarkMode()
+{
+    if (_markMode)
+    {
+        // Exit Mark Mode
+        ClearSelection();
+    }
+    else
+    {
+        // Enter Mark Mode
+        // NOTE: directly set cursor state. We already should have locked before calling this function.
+        _activeBuffer().GetCursor().SetIsOn(false);
+        const auto cursorPos{ _activeBuffer().GetCursor().GetPosition() };
+        _selection = SelectionAnchors{};
+        _selection->start = cursorPos;
+        _selection->end = cursorPos;
+        _selection->pivot = cursorPos;
+        _markMode = true;
+    }
+}
+
+Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams(const ControlKeyStates mods, const WORD vkey) const
+{
+    if ((_markMode || mods.IsShiftPressed()) && !mods.IsAltPressed())
     {
         if (mods.IsCtrlPressed())
         {
             // Ctrl + Shift + _
+            // (Mark Mode) Ctrl + _
             switch (vkey)
             {
             case VK_LEFT:
@@ -257,6 +284,7 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
         else
         {
             // Shift + _
+            // (Mark Mode) Just the vkeys
             switch (vkey)
             {
             case VK_HOME:
@@ -281,11 +309,19 @@ Terminal::UpdateSelectionParams Terminal::ConvertKeyEventToUpdateSelectionParams
     return std::nullopt;
 }
 
-void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode)
+// Method Description:
+// - updates the selection endpoints based on a direction and expansion mode. Primarily used for keyboard selection.
+// Arguments:
+// - direction: the direction to move the selection endpoint in
+// - mode: the type of movement to be performed (i.e. move by word)
+// - mods: the key modifiers pressed when performing this update
+void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion mode, ControlKeyStates mods)
 {
     // 1. Figure out which endpoint to update
-    // One of the endpoints is the pivot, signifying that the other endpoint is the one we want to move.
-    const auto movingEnd{ _selection->start == _selection->pivot };
+    // If we're in mark mode, shift dictates whether you are moving the end or not.
+    // Otherwise, we're updating an existing selection, so one of the endpoints is the pivot,
+    //   signifying that the other endpoint is the one we want to move.
+    const auto movingEnd{ _markMode ? mods.IsShiftPressed() : _selection->start == _selection->pivot };
     auto targetPos{ movingEnd ? _selection->end : _selection->start };
 
     // 2. Perform the movement
@@ -307,8 +343,23 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion
 
     // 3. Actually modify the selection
     // NOTE: targetStart doesn't matter here
-    auto targetStart = false;
-    std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
+    if (_markMode)
+    {
+        // [Mark Mode]
+        // - moveSelectionEnd  --> just move end (i.e. shift + arrow keys)
+        // - !moveSelectionEnd --> move all three (i.e. just use arrow keys)
+        _selection->end = targetPos;
+        if (!movingEnd)
+        {
+            _selection->start = targetPos;
+            _selection->pivot = targetPos;
+        }
+    }
+    else
+    {
+        auto targetStart = false;
+        std::tie(_selection->start, _selection->end) = _PivotSelection(targetPos, targetStart);
+    }
 
     // 4. Scroll (if necessary)
     if (const auto viewport = _GetVisibleViewport(); !viewport.IsInBounds(targetPos))
@@ -473,6 +524,7 @@ void Terminal::_MoveByBuffer(SelectionDirection direction, COORD& pos)
 void Terminal::ClearSelection()
 {
     _selection = std::nullopt;
+    _markMode = false;
 }
 
 // Method Description:
diff --git a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
index 15d48a51e50..517c85551df 100644
--- a/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
+++ b/src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp
@@ -76,6 +76,7 @@ static constexpr std::string_view QuitKey{ "quit" };
 static constexpr std::string_view AdjustOpacityKey{ "adjustOpacity" };
 static constexpr std::string_view RestoreLastClosedKey{ "restoreLastClosed" };
 static constexpr std::string_view SelectAllKey{ "selectAll" };
+static constexpr std::string_view MarkModeKey{ "markMode" };
 
 static constexpr std::string_view ActionKey{ "action" };
 
@@ -388,6 +389,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
                 { ShortcutAction::AdjustOpacity, L"" }, // Intentionally omitted, must be generated by GenerateName
                 { ShortcutAction::RestoreLastClosed, RS_(L"RestoreLastClosedCommandKey") },
                 { ShortcutAction::SelectAll, RS_(L"SelectAllCommandKey") },
+                { ShortcutAction::MarkMode, RS_(L"MarkModeCommandKey") },
             };
         }();
 
diff --git a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
index d988fc234a4..24f04b232a1 100644
--- a/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
+++ b/src/cascadia/TerminalSettingsModel/AllShortcutActions.h
@@ -89,7 +89,8 @@
     ON_ALL_ACTIONS(Quit)                   \
     ON_ALL_ACTIONS(AdjustOpacity)          \
     ON_ALL_ACTIONS(RestoreLastClosed)      \
-    ON_ALL_ACTIONS(SelectAll)
+    ON_ALL_ACTIONS(SelectAll)              \
+    ON_ALL_ACTIONS(MarkMode)
 
 #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 314ecf8f8bf..7a23d7a111e 100644
--- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw
@@ -525,4 +525,8 @@
   <data name="SelectAllCommandKey" xml:space="preserve">
     <value>Select all text</value>
   </data>
+  <data name="MarkModeCommandKey" xml:space="preserve">
+    <value>Toggle mark mode</value>
+    <comment>A command that will toggle "mark mode". This is a mode in the application where the user can mark the text by selecting portions of the text.</comment>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json
index 7a5c2d1f9d5..95118b5d884 100644
--- a/src/cascadia/TerminalSettingsModel/defaults.json
+++ b/src/cascadia/TerminalSettingsModel/defaults.json
@@ -385,6 +385,7 @@
         { "command": "paste", "keys": "ctrl+shift+v" },
         { "command": "paste", "keys": "shift+insert" },
         { "command": "selectAll", "keys": "ctrl+shift+a" },
+        { "command": "markMode", "keys": "ctrl+shift+m" },
 
         // Scrollback
         { "command": "scrollDown", "keys": "ctrl+shift+down" },


Test patch: