Add `multi_cursor_modifier` setting (#9014)

Max and Conrad Irwin created

this PR allows users to use `cmd` instead of `alt` as the
`multi_cursor_modifier` for creating multiple cursors/selections

closes #4339

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

assets/settings/default.json         |  3 +++
crates/editor/src/editor_settings.rs | 17 +++++++++++++++--
crates/editor/src/element.rs         | 17 ++++++++++++++---
3 files changed, 32 insertions(+), 5 deletions(-)

Detailed changes

assets/settings/default.json 🔗

@@ -47,6 +47,9 @@
   // The factor to grow the active pane by. Defaults to 1.0
   // which gives the same size as all other panes.
   "active_pane_magnification": 1.0,
+  // The key to use for adding multiple cursors
+  // Currently "alt" or "cmd" are supported.
+  "multi_cursor_modifier": "alt",
   // Whether to enable vim modes and key bindings
   "vim_mode": false,
   // Whether to show the informational hover box when moving the mouse

crates/editor/src/editor_settings.rs 🔗

@@ -16,6 +16,7 @@ pub struct EditorSettings {
     pub vertical_scroll_margin: f32,
     pub relative_line_numbers: bool,
     pub seed_search_query_from_cursor: SeedQuerySetting,
+    pub multi_cursor_modifier: MultiCursorModifier,
     pub redact_private_values: bool,
     #[serde(default)]
     pub double_click_in_multibuffer: DoubleClickInMultibuffer,
@@ -84,6 +85,16 @@ pub enum ShowScrollbar {
     Never,
 }
 
+/// The key to use for adding multiple cursors
+///
+/// Default: alt
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum MultiCursorModifier {
+    Alt,
+    Cmd,
+}
+
 #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
 pub struct EditorSettingsContent {
     /// Whether the cursor blinks in the editor.
@@ -121,7 +132,6 @@ pub struct EditorSettingsContent {
     pub scrollbar: Option<ScrollbarContent>,
     /// Gutter related settings
     pub gutter: Option<GutterContent>,
-
     /// The number of lines to keep above/below the cursor when auto-scrolling.
     ///
     /// Default: 3.
@@ -134,7 +144,10 @@ pub struct EditorSettingsContent {
     ///
     /// Default: always
     pub seed_search_query_from_cursor: Option<SeedQuerySetting>,
-
+    /// The key to use for adding multiple cursors
+    ///
+    /// Default: alt
+    pub multi_cursor_modifier: Option<MultiCursorModifier>,
     /// Hide the values of variables in `private` files, as defined by the
     /// private_files setting. This only changes the visual representation,
     /// the values are still present in the file and can be selected / copied / pasted

crates/editor/src/element.rs 🔗

@@ -3,7 +3,7 @@ use crate::{
         BlockContext, BlockStyle, DisplaySnapshot, FoldStatus, HighlightedChunk, ToDisplayPoint,
         TransformBlock,
     },
-    editor_settings::{DoubleClickInMultibuffer, ShowScrollbar},
+    editor_settings::{DoubleClickInMultibuffer, MultiCursorModifier, ShowScrollbar},
     git::{diff_hunk_to_display, DisplayDiffHunk},
     hover_popover::{
         self, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
@@ -455,10 +455,15 @@ impl EditorElement {
                 cx,
             );
         } else {
+            let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
+            let multi_cursor_modifier = match multi_cursor_setting {
+                MultiCursorModifier::Alt => modifiers.alt,
+                MultiCursorModifier::Cmd => modifiers.command,
+            };
             editor.select(
                 SelectPhase::Begin {
                     position,
-                    add: modifiers.alt,
+                    add: multi_cursor_modifier,
                     click_count,
                 },
                 cx,
@@ -503,7 +508,13 @@ impl EditorElement {
             editor.select(SelectPhase::End, cx);
         }
 
-        if !pending_nonempty_selections && event.modifiers.command && text_hitbox.is_hovered(cx) {
+        let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
+        let multi_cursor_modifier = match multi_cursor_setting {
+            MultiCursorModifier::Alt => event.modifiers.command,
+            MultiCursorModifier::Cmd => event.modifiers.alt,
+        };
+
+        if !pending_nonempty_selections && multi_cursor_modifier && text_hitbox.is_hovered(cx) {
             let point = position_map.point_for_position(text_hitbox.bounds, event.position);
             editor.handle_click_hovered_link(point, event.modifiers, cx);