editor: Fix first `cmd-left` target for cursor in leading whitespace (#36145)

smit created

Closes #35805

If the cursor is between column 0 and the indent size, pressing
`cmd-left` jumps to the indent. Pressing it again moves to the true
column 0. Further presses toggle between indent and column 0.

This PR changes the first `cmd-left` to go to column 0 instead of
indent. Toggling between is unaffected.

Release Notes:

- Fixed issue where pressing `cmd-left` with the cursor in the leading
spaces moved to the start of the text first. It now goes to the
beginning of the line first, then the start of the text.

Change summary

crates/editor/src/editor_tests.rs | 45 +++++++++++++++++++++++++++++++++
crates/editor/src/movement.rs     |  2 
2 files changed, 46 insertions(+), 1 deletion(-)

Detailed changes

crates/editor/src/editor_tests.rs 🔗

@@ -1901,6 +1901,51 @@ fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) {
     });
 }
 
+#[gpui::test]
+fn test_beginning_of_line_with_cursor_between_line_start_and_indent(cx: &mut TestAppContext) {
+    init_test(cx, |_| {});
+
+    let move_to_beg = MoveToBeginningOfLine {
+        stop_at_soft_wraps: true,
+        stop_at_indent: true,
+    };
+
+    let editor = cx.add_window(|window, cx| {
+        let buffer = MultiBuffer::build_simple("    hello\nworld", cx);
+        build_editor(buffer, window, cx)
+    });
+
+    _ = editor.update(cx, |editor, window, cx| {
+        // test cursor between line_start and indent_start
+        editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
+            s.select_display_ranges([
+                DisplayPoint::new(DisplayRow(0), 3)..DisplayPoint::new(DisplayRow(0), 3)
+            ]);
+        });
+
+        // cursor should move to line_start
+        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
+        assert_eq!(
+            editor.selections.display_ranges(cx),
+            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
+        );
+
+        // cursor should move to indent_start
+        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
+        assert_eq!(
+            editor.selections.display_ranges(cx),
+            &[DisplayPoint::new(DisplayRow(0), 4)..DisplayPoint::new(DisplayRow(0), 4)]
+        );
+
+        // cursor should move to back to line_start
+        editor.move_to_beginning_of_line(&move_to_beg, window, cx);
+        assert_eq!(
+            editor.selections.display_ranges(cx),
+            &[DisplayPoint::new(DisplayRow(0), 0)..DisplayPoint::new(DisplayRow(0), 0)]
+        );
+    });
+}
+
 #[gpui::test]
 fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
     init_test(cx, |_| {});

crates/editor/src/movement.rs 🔗

@@ -230,7 +230,7 @@ pub fn indented_line_beginning(
     if stop_at_soft_boundaries && soft_line_start > indent_start && display_point != soft_line_start
     {
         soft_line_start
-    } else if stop_at_indent && display_point != indent_start {
+    } else if stop_at_indent && (display_point > indent_start || display_point == line_start) {
         indent_start
     } else {
         line_start