Add toggle case command (#28415)

Joseph T. Lyons created

A small addition for those coming from JetBrain's IDEs. A behavioral
detail: when any upper case character is detected, the command defaults
to toggling to lower case.

> Note that when you apply the toggle case action to the CamelCase name
format, IntelliJ IDEA converts the name to the lower case.


https://www.jetbrains.com/help/idea/working-with-source-code.html#edit_code_fragments

Release Notes:

- Added an `editor: toggle case` command. Use `cmd-shift-u` for macOS
and `ctrl-shift-u` for Linux, when using the `JetBrains` keymap.

Change summary

assets/keymaps/linux/jetbrains.json |  3 +
assets/keymaps/macos/jetbrains.json |  3 +
crates/editor/src/actions.rs        |  1 
crates/editor/src/editor.rs         | 11 +++++++++
crates/editor/src/editor_tests.rs   | 35 ++++++++++++++++++++++++++++++
crates/editor/src/element.rs        |  1 
6 files changed, 52 insertions(+), 2 deletions(-)

Detailed changes

assets/keymaps/linux/jetbrains.json 🔗

@@ -58,7 +58,8 @@
       "ctrl-shift-home": "editor::SelectToBeginning",
       "ctrl-shift-end": "editor::SelectToEnd",
       "ctrl-f8": "editor::ToggleBreakpoint",
-      "ctrl-shift-f8": "editor::EditLogBreakpoint"
+      "ctrl-shift-f8": "editor::EditLogBreakpoint",
+      "ctrl-shift-u": "editor::ToggleCase"
     }
   },
   {

assets/keymaps/macos/jetbrains.json 🔗

@@ -55,7 +55,8 @@
       "cmd-shift-home": "editor::SelectToBeginning",
       "cmd-shift-end": "editor::SelectToEnd",
       "ctrl-f8": "editor::ToggleBreakpoint",
-      "ctrl-shift-f8": "editor::EditLogBreakpoint"
+      "ctrl-shift-f8": "editor::EditLogBreakpoint",
+      "cmd-shift-u": "editor::ToggleCase"
     }
   },
   {

crates/editor/src/actions.rs 🔗

@@ -420,6 +420,7 @@ actions!(
         Tab,
         Backtab,
         ToggleBreakpoint,
+        ToggleCase,
         DisableBreakpoint,
         EnableBreakpoint,
         EditLogBreakpoint,

crates/editor/src/editor.rs 🔗

@@ -9143,6 +9143,17 @@ impl Editor {
         });
     }
 
+    pub fn toggle_case(&mut self, _: &ToggleCase, window: &mut Window, cx: &mut Context<Self>) {
+        self.manipulate_text(window, cx, |text| {
+            let has_upper_case_characters = text.chars().any(|c| c.is_uppercase());
+            if has_upper_case_characters {
+                text.to_lowercase()
+            } else {
+                text.to_uppercase()
+            }
+        })
+    }
+
     pub fn convert_to_upper_case(
         &mut self,
         _: &ConvertToUpperCase,

crates/editor/src/editor_tests.rs 🔗

@@ -3875,6 +3875,41 @@ async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) {
     "});
 }
 
+#[gpui::test]
+async fn test_toggle_case(cx: &mut TestAppContext) {
+    init_test(cx, |_| {});
+
+    let mut cx = EditorTestContext::new(cx).await;
+
+    // If all lower case -> upper case
+    cx.set_state(indoc! {"
+        «hello worldˇ»
+    "});
+    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
+    cx.assert_editor_state(indoc! {"
+        «HELLO WORLDˇ»
+    "});
+
+    // If all upper case -> lower case
+    cx.set_state(indoc! {"
+        «HELLO WORLDˇ»
+    "});
+    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
+    cx.assert_editor_state(indoc! {"
+        «hello worldˇ»
+    "});
+
+    // If any upper case characters are identified -> lower case
+    // This matches JetBrains IDEs
+    cx.set_state(indoc! {"
+        «hEllo worldˇ»
+    "});
+    cx.update_editor(|e, window, cx| e.toggle_case(&ToggleCase, window, cx));
+    cx.assert_editor_state(indoc! {"
+        «hello worldˇ»
+    "});
+}
+
 #[gpui::test]
 async fn test_manipulate_text(cx: &mut TestAppContext) {
     init_test(cx, |_| {});

crates/editor/src/element.rs 🔗

@@ -211,6 +211,7 @@ impl EditorElement {
         register_action(editor, window, Editor::sort_lines_case_insensitive);
         register_action(editor, window, Editor::reverse_lines);
         register_action(editor, window, Editor::shuffle_lines);
+        register_action(editor, window, Editor::toggle_case);
         register_action(editor, window, Editor::convert_to_upper_case);
         register_action(editor, window, Editor::convert_to_lower_case);
         register_action(editor, window, Editor::convert_to_title_case);