Add emacs keybindings for mark emulation (#22904)

Ozan and Peter Tripp created

These keybindings extend the already selected text. This allows closer
emacs emulation where subsequent movement commands extend / shrink the
current selection instead of dismissing it.

This is a follow up on 
- #21927

Release Notes:

- Added emacs movement keybindings that extend/shrink the current
selection

---------

Co-authored-by: Peter Tripp <peter@zed.dev>

Change summary

assets/keymaps/linux/emacs.json | 61 +++++++++++++++++++++++++++++++++-
assets/keymaps/macos/emacs.json | 61 +++++++++++++++++++++++++++++++++-
2 files changed, 118 insertions(+), 4 deletions(-)

Detailed changes

assets/keymaps/linux/emacs.json 🔗

@@ -15,7 +15,6 @@
       "ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
       "alt-g g": "go_to_line::Toggle", // goto-line
       "alt-g alt-g": "go_to_line::Toggle", // goto-line
-      //"ctrl-space": "editor::SetMark",
       "ctrl-f": "editor::MoveRight", // forward-char
       "ctrl-b": "editor::MoveLeft", // backward-char
       "ctrl-n": "editor::MoveDown", // next-line
@@ -52,7 +51,65 @@
       "alt->": "editor::MoveToEnd", // end-of-buffer
       "ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
       "ctrl-s": "buffer_search::Deploy", // isearch-forward
-      "alt-^": "editor::JoinLines" // join-line
+      "alt-^": "editor::JoinLines", // join-line
+      "alt-/": "editor::ShowCompletions" // dabbrev-expand
+    }
+  },
+  // Extend selection with movement bindings
+  {
+    "context": "Editor && (mode == full) && selection",
+    "bindings": {
+      "right": "editor::SelectRight",
+      "left": "editor::SelectLeft",
+      "down": "editor::SelectDown",
+      "up": "editor::SelectUp",
+      "home": "editor::SelectToBeginningOfLine",
+      "end": "editor::SelectToEndOfLine",
+      "alt-left": "editor::SelectToPreviousWordStart",
+      "alt-right": "editor::SelectToNextWordEnd",
+      "pagedown": "editor::SelectPageDown",
+      "pageup": "editor::SelectPageUp",
+      "ctrl-f": "editor::SelectRight",
+      "ctrl-b": "editor::SelectLeft",
+      "ctrl-n": "editor::SelectDown",
+      "ctrl-p": "editor::SelectUp",
+      "ctrl-a": "editor::SelectToBeginningOfLine",
+      "ctrl-e": "editor::SelectToEndOfLine",
+      "alt-f": "editor::SelectToNextWordEnd",
+      "alt-b": "editor::SelectToPreviousSubwordStart",
+      "alt-<": "editor::SelectToBeginning",
+      "alt->": "editor::SelectToEnd",
+      "ctrl-space": "editor::Cancel" // clear mark
+    }
+  },
+  // Emacs set-mark-command emulation (ctrl-space + movement bindings)
+  {
+    "context": "Editor && (mode == full) && !selection",
+    "bindings": {
+      "ctrl-space right": "editor::SelectRight",
+      "ctrl-space left": "editor::SelectLeft",
+      "ctrl-space down": "editor::SelectDown",
+      "ctrl-space up": "editor::SelectUp",
+      "ctrl-space home": "editor::SelectToBeginningOfLine",
+      "ctrl-space end": "editor::SelectToEndOfLine",
+      "ctrl-space alt-left": "editor::SelectToPreviousWordStart",
+      "ctrl-space alt-right": "editor::SelectToNextWordEnd",
+      "ctrl-space pagedown": "editor::SelectPageDown",
+      "ctrl-space pageup": "editor::SelectPageUp",
+      "ctrl-space ctrl-f": "editor::SelectRight",
+      "ctrl-space ctrl-b": "editor::SelectLeft",
+      "ctrl-space ctrl-n": "editor::SelectDown",
+      "ctrl-space ctrl-p": "editor::SelectUp",
+      "ctrl-space ctrl-a": "editor::SelectToBeginningOfLine",
+      "ctrl-space ctrl-e": "editor::SelectToEndOfLine",
+      "ctrl-space alt-f": "editor::SelectToNextWordEnd",
+      "ctrl-space alt-b": "editor::SelectToPreviousWordStart",
+      "ctrl-space alt-<": "editor::SelectToBeginning",
+      "ctrl-space alt->": "editor::SelectToEnd",
+      "ctrl-space alt-v": "editor::SelectPageUp",
+      "ctrl-space ctrl-v": "editor::SelectPageDown",
+      "ctrl-space alt-{": "editor::SelectToStartOfParagraph",
+      "ctrl-space alt-}": "editor::SelectToEndOfParagraph"
     }
   },
   {

assets/keymaps/macos/emacs.json 🔗

@@ -15,7 +15,6 @@
       "ctrl-x b": "tab_switcher::Toggle", // switch-to-buffer
       "alt-g g": "go_to_line::Toggle", // goto-line
       "alt-g alt-g": "go_to_line::Toggle", // goto-line
-      //"ctrl-space": "editor::SetMark",
       "ctrl-f": "editor::MoveRight", // forward-char
       "ctrl-b": "editor::MoveLeft", // backward-char
       "ctrl-n": "editor::MoveDown", // next-line
@@ -52,7 +51,65 @@
       "alt->": "editor::MoveToEnd", // end-of-buffer
       "ctrl-l": "editor::ScrollCursorCenterTopBottom", // recenter-top-bottom
       "ctrl-s": "buffer_search::Deploy", // isearch-forward
-      "alt-^": "editor::JoinLines" // join-line
+      "alt-^": "editor::JoinLines", // join-line
+      "alt-/": "editor::ShowCompletions" // dabbrev-expand
+    }
+  },
+  // Extend selection with movement bindings
+  {
+    "context": "Editor && (mode == full) && selection",
+    "bindings": {
+      "right": "editor::SelectRight",
+      "left": "editor::SelectLeft",
+      "down": "editor::SelectDown",
+      "up": "editor::SelectUp",
+      "home": "editor::SelectToBeginningOfLine",
+      "end": "editor::SelectToEndOfLine",
+      "alt-left": "editor::SelectToPreviousWordStart",
+      "alt-right": "editor::SelectToNextWordEnd",
+      "pagedown": "editor::SelectPageDown",
+      "pageup": "editor::SelectPageUp",
+      "ctrl-f": "editor::SelectRight",
+      "ctrl-b": "editor::SelectLeft",
+      "ctrl-n": "editor::SelectDown",
+      "ctrl-p": "editor::SelectUp",
+      "ctrl-a": "editor::SelectToBeginningOfLine",
+      "ctrl-e": "editor::SelectToEndOfLine",
+      "alt-f": "editor::SelectToNextWordEnd",
+      "alt-b": "editor::SelectToPreviousSubwordStart",
+      "alt-<": "editor::SelectToBeginning",
+      "alt->": "editor::SelectToEnd",
+      "ctrl-space": "editor::Cancel" // clear mark
+    }
+  },
+  // Emacs set-mark-command emulation (ctrl-space + movement bindings)
+  {
+    "context": "Editor && (mode == full) && !selection",
+    "bindings": {
+      "ctrl-space right": "editor::SelectRight",
+      "ctrl-space left": "editor::SelectLeft",
+      "ctrl-space down": "editor::SelectDown",
+      "ctrl-space up": "editor::SelectUp",
+      "ctrl-space home": "editor::SelectToBeginningOfLine",
+      "ctrl-space end": "editor::SelectToEndOfLine",
+      "ctrl-space alt-left": "editor::SelectToPreviousWordStart",
+      "ctrl-space alt-right": "editor::SelectToNextWordEnd",
+      "ctrl-space pagedown": "editor::SelectPageDown",
+      "ctrl-space pageup": "editor::SelectPageUp",
+      "ctrl-space ctrl-f": "editor::SelectRight",
+      "ctrl-space ctrl-b": "editor::SelectLeft",
+      "ctrl-space ctrl-n": "editor::SelectDown",
+      "ctrl-space ctrl-p": "editor::SelectUp",
+      "ctrl-space ctrl-a": "editor::SelectToBeginningOfLine",
+      "ctrl-space ctrl-e": "editor::SelectToEndOfLine",
+      "ctrl-space alt-f": "editor::SelectToNextWordEnd",
+      "ctrl-space alt-b": "editor::SelectToPreviousWordStart",
+      "ctrl-space alt-<": "editor::SelectToBeginning",
+      "ctrl-space alt->": "editor::SelectToEnd",
+      "ctrl-space alt-v": "editor::SelectPageUp",
+      "ctrl-space ctrl-v": "editor::SelectPageDown",
+      "ctrl-space alt-{": "editor::SelectToStartOfParagraph",
+      "ctrl-space alt-}": "editor::SelectToEndOfParagraph"
     }
   },
   {