Allow joining multiple lines and deleting empty lines

Nathan Sobo and Conrad Irwin created

Co-Authored-By: Conrad Irwin <conrad.irwin@gmail.com>

Change summary

crates/editor/src/editor.rs       | 33 +++++++++++++++++++++++----
crates/editor/src/editor_tests.rs | 39 +++++++++++++++++++++++++++-----
2 files changed, 60 insertions(+), 12 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -3955,13 +3955,36 @@ impl Editor {
     }
 
     pub fn join_lines(&mut self, _: &JoinLines, cx: &mut ViewContext<Self>) {
-        let cursor_position = self.selections.newest::<Point>(cx).head();
+        let selection = self.selections.newest::<Point>(cx);
         let snapshot = self.buffer.read(cx).snapshot(cx);
-        let end_of_line = Point::new(cursor_position.row, snapshot.line_len(cursor_position.row));
-        let start_of_next_line = end_of_line + Point::new(1, 0);
 
-        self.buffer.update(cx, |buffer, cx| {
-            buffer.edit([(end_of_line..start_of_next_line, " ")], None, cx)
+        let row_range = if selection.start.row == selection.end.row {
+            selection.start.row..selection.start.row + 1
+        } else {
+            selection.start.row..selection.end.row
+        };
+
+        self.transact(cx, |this, cx| {
+            for (ix, row) in row_range.rev().enumerate() {
+                let end_of_line = Point::new(row, snapshot.line_len(row));
+                let start_of_next_line = end_of_line + Point::new(1, 0);
+
+                let replace = if snapshot.line_len(row + 1) > 0 {
+                    " "
+                } else {
+                    ""
+                };
+
+                this.buffer.update(cx, |buffer, cx| {
+                    buffer.edit([(end_of_line..start_of_next_line, replace)], None, cx)
+                });
+
+                if ix == 0 {
+                    this.change_selections(Some(Autoscroll::fit()), cx, |s| {
+                        s.select_ranges([end_of_line..end_of_line])
+                    })
+                }
+            }
         });
     }
 

crates/editor/src/editor_tests.rs 🔗

@@ -2332,8 +2332,8 @@ fn test_delete_line(cx: &mut TestAppContext) {
 fn test_join_lines(cx: &mut TestAppContext) {
     init_test(cx, |_| {});
 
-    let (_, editor) = cx.add_window(|cx| {
-        let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
+    cx.add_window(|cx| {
+        let buffer = MultiBuffer::build_simple("aaa\nbbb\nccc\nddd\n\n", cx);
         let mut editor = build_editor(buffer.clone(), cx);
         let buffer = buffer.read(cx).as_singleton().unwrap();
 
@@ -2343,13 +2343,38 @@ fn test_join_lines(cx: &mut TestAppContext) {
         );
 
         editor.join_lines(&JoinLines, cx);
+        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
+        assert_eq!(
+            editor.selections.ranges::<Point>(cx),
+            &[Point::new(0, 3)..Point::new(0, 3)]
+        );
 
-        // assert_eq!(
-        //     editor.selections.ranges::<Point>(cx),
-        //     &[Point::new(0, 0), Point::new(0, 0)]
-        // );
+        editor.change_selections(None, cx, |s| {
+            s.select_ranges([Point::new(0, 5)..Point::new(2, 2)])
+        });
+        editor.join_lines(&JoinLines, cx);
+        assert_eq!(buffer.read(cx).text(), "aaa bbb ccc ddd\n\n");
+        assert_eq!(
+            editor.selections.ranges::<Point>(cx),
+            &[Point::new(0, 11)..Point::new(0, 11)]
+        );
 
-        assert_eq!(buffer.read(cx).text(), "abc def\nghi\n");
+        editor.undo(&Undo, cx);
+        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n\n");
+        assert_eq!(
+            editor.selections.ranges::<Point>(cx),
+            &[Point::new(0, 5)..Point::new(2, 2)]
+        );
+
+        editor.change_selections(None, cx, |s| {
+            s.select_ranges([Point::new(2, 1)..Point::new(2, 2)])
+        });
+        editor.join_lines(&JoinLines, cx);
+        assert_eq!(buffer.read(cx).text(), "aaa bbb\nccc\nddd\n");
+        assert_eq!(
+            editor.selections.ranges::<Point>(cx),
+            [Point::new(2, 3)..Point::new(2, 3)]
+        );
 
         editor
     });