diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 26d1b213629ef921aeb6ee6d67838ced9d6fb7bb..db84a1916c24657d60bc9052cc5a5e5df5b48a56 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -35,6 +35,13 @@ pub struct SelectToBeginningOfLine { pub stop_at_indent: bool, } +#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct DeleteToBeginningOfLine { + #[serde(default)] + pub(super) stop_at_indent: bool, +} + #[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)] #[serde(deny_unknown_fields)] pub struct MovePageUp { @@ -226,6 +233,7 @@ impl_actions!( ComposeCompletion, ConfirmCodeAction, ConfirmCompletion, + DeleteToBeginningOfLine, DeleteToNextWordEnd, DeleteToPreviousWordStart, ExpandExcerpts, @@ -292,7 +300,6 @@ gpui::actions!( CutToEndOfLine, Delete, DeleteLine, - DeleteToBeginningOfLine, DeleteToEndOfLine, DeleteToNextSubwordEnd, DeleteToPreviousSubwordStart, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 42fb328b436fb4afe18731d95cab705ba273c084..fcc06861fc60022dde6b3168e511f4fb8aa7c632 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -9516,7 +9516,7 @@ impl Editor { pub fn delete_to_beginning_of_line( &mut self, - _: &DeleteToBeginningOfLine, + action: &DeleteToBeginningOfLine, window: &mut Window, cx: &mut Context, ) { @@ -9530,7 +9530,7 @@ impl Editor { this.select_to_beginning_of_line( &SelectToBeginningOfLine { stop_at_soft_wraps: false, - stop_at_indent: false, + stop_at_indent: action.stop_at_indent, }, window, cx, diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 26ad45cfab3eda6e7b5104cebd02715626050c35..0efe03ead05a3e0edaea1406f7e8e56face16863 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1514,6 +1514,10 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) { stop_at_indent: true, }; + let delete_to_beg = DeleteToBeginningOfLine { + stop_at_indent: false, + }; + let move_to_end = MoveToEndOfLine { stop_at_soft_wraps: true, }; @@ -1672,7 +1676,7 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) { }); _ = editor.update(cx, |editor, window, cx| { - editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx); + editor.delete_to_beginning_of_line(&delete_to_beg, window, cx); assert_eq!(editor.display_text(cx), "\n"); assert_eq!( editor.selections.display_ranges(cx), @@ -1778,6 +1782,107 @@ fn test_beginning_end_of_line_ignore_soft_wrap(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn test_beginning_of_line_stop_at_indent(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + let move_to_beg = MoveToBeginningOfLine { + stop_at_soft_wraps: true, + stop_at_indent: true, + }; + + let select_to_beg = SelectToBeginningOfLine { + stop_at_soft_wraps: true, + stop_at_indent: true, + }; + + let delete_to_beg = DeleteToBeginningOfLine { + stop_at_indent: true, + }; + + let move_to_end = MoveToEndOfLine { + stop_at_soft_wraps: false, + }; + + let editor = cx.add_window(|window, cx| { + let buffer = MultiBuffer::build_simple("abc\n def", cx); + build_editor(buffer, window, cx) + }); + + _ = editor.update(cx, |editor, window, cx| { + editor.change_selections(None, window, cx, |s| { + s.select_display_ranges([ + DisplayPoint::new(DisplayRow(0), 1)..DisplayPoint::new(DisplayRow(0), 1), + DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 4), + ]); + }); + + // Moving to the beginning of the line should put the first cursor at the beginning of the line, + // and the second cursor at the first non-whitespace character in the line. + 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), + DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2), + ] + ); + + // Moving to the beginning of the line again should be a no-op for the first cursor, + // and should move the second cursor to the beginning of the line. + 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), + DisplayPoint::new(DisplayRow(1), 0)..DisplayPoint::new(DisplayRow(1), 0), + ] + ); + + // Moving to the beginning of the line again should still be a no-op for the first cursor, + // and should move the second cursor back to the first non-whitespace character in the line. + 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), + DisplayPoint::new(DisplayRow(1), 2)..DisplayPoint::new(DisplayRow(1), 2), + ] + ); + + // Selecting to the beginning of the line should select to the beginning of the line for the first cursor, + // and to the first non-whitespace character in the line for the second cursor. + editor.move_to_end_of_line(&move_to_end, window, cx); + editor.move_left(&MoveLeft, window, cx); + editor.select_to_beginning_of_line(&select_to_beg, window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + &[ + DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0), + DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 2), + ] + ); + + // Selecting to the beginning of the line again should be a no-op for the first cursor, + // and should select to the beginning of the line for the second cursor. + editor.select_to_beginning_of_line(&select_to_beg, window, cx); + assert_eq!( + editor.selections.display_ranges(cx), + &[ + DisplayPoint::new(DisplayRow(0), 2)..DisplayPoint::new(DisplayRow(0), 0), + DisplayPoint::new(DisplayRow(1), 4)..DisplayPoint::new(DisplayRow(1), 0), + ] + ); + + // Deleting to the beginning of the line should delete to the beginning of the line for the first cursor, + // and should delete to the first non-whitespace character in the line for the second cursor. + editor.move_to_end_of_line(&move_to_end, window, cx); + editor.move_left(&MoveLeft, window, cx); + editor.delete_to_beginning_of_line(&delete_to_beg, window, cx); + assert_eq!(editor.text(cx), "c\n f"); + }); +} + #[gpui::test] fn test_prev_next_word_boundary(cx: &mut TestAppContext) { init_test(cx, |_| {}); @@ -2295,7 +2400,13 @@ async fn test_delete_to_beginning_of_line(cx: &mut TestAppContext) { let mut cx = EditorTestContext::new(cx).await; cx.set_state("one «two threeˇ» four"); cx.update_editor(|editor, window, cx| { - editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, window, cx); + editor.delete_to_beginning_of_line( + &DeleteToBeginningOfLine { + stop_at_indent: false, + }, + window, + cx, + ); assert_eq!(editor.text(cx), " four"); }); }