From 1a8438288128bdda84dc35e6efc958aa8415b70e Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Mon, 24 Jul 2023 22:30:51 -0400 Subject: [PATCH 1/5] WIP --- crates/editor/src/editor.rs | 103 ++++++++++++++++++++++++++++++ crates/editor/src/editor_tests.rs | 79 +++++++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index f1cf5a942b05da76cf7a85046f3a8925bf375523..fe7667cd2badfef7010dbce7de70e1db5048b1ca 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -74,6 +74,8 @@ pub use multi_buffer::{ }; use ordered_float::OrderedFloat; use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction}; +use rand::seq::SliceRandom; +use rand::thread_rng; use scroll::{ autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, }; @@ -226,6 +228,10 @@ actions!( MoveLineUp, MoveLineDown, JoinLines, + SortLinesCaseSensitive, + SortLinesCaseInsensitive, + ReverseLines, + ShuffleLines, Transpose, Cut, Copy, @@ -344,6 +350,10 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::outdent); cx.add_action(Editor::delete_line); cx.add_action(Editor::join_lines); + cx.add_action(Editor::sort_lines_case_sensitive); + cx.add_action(Editor::sort_lines_case_insensitive); + cx.add_action(Editor::reverse_lines); + // cx.add_action(Editor::shuffle_lines); cx.add_action(Editor::delete_to_previous_word_start); cx.add_action(Editor::delete_to_previous_subword_start); cx.add_action(Editor::delete_to_next_word_end); @@ -4205,6 +4215,99 @@ impl Editor { }); } + pub fn sort_lines_case_sensitive( + &mut self, + _: &SortLinesCaseSensitive, + cx: &mut ViewContext, + ) { + self.manipulate_lines(cx, { + |mut lines| { + lines.sort(); + lines + } + }) + } + + pub fn sort_lines_case_insensitive( + &mut self, + _: &SortLinesCaseInsensitive, + cx: &mut ViewContext, + ) { + self.manipulate_lines(cx, { + |mut lines| { + lines.sort_by_key(|line| line.to_lowercase()); + lines + } + }) + } + + pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { + self.manipulate_lines(cx, { + |mut lines| { + lines.reverse(); + lines + } + }) + } + + // pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { + // self.manipulate_lines(cx, { + // |mut lines| { + // lines.shuffle(&mut thread_rng()); + // lines + // } + // }) + // } + + fn manipulate_lines(&mut self, cx: &mut ViewContext, mut callback: Fn) + where + Fn: FnMut(Vec<&str>) -> Vec<&str>, + { + let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); + let buffer = self.buffer.read(cx).snapshot(cx); + + let mut edits = Vec::new(); + + let selections = self.selections.all::(cx); + let mut selections = selections.iter().peekable(); + let mut contiguous_row_selections = Vec::new(); + + while let Some(selection) = selections.next() { + // Find all the selections that span a contiguous row range + let (start_row, end_row) = consume_contiguous_rows( + &mut contiguous_row_selections, + selection, + &display_map, + &mut selections, + ); + let start = Point::new(start_row, 0); + let end = Point::new(end_row - 1, buffer.line_len(end_row - 1)); + let text = buffer.text_for_range(start..end).collect::(); + // TODO SORT LINES: Is there a smarter / more effificent way to obtain lines? + let lines = text.split("\n").collect::>(); + let lines = callback(lines); + edits.push((start..end, lines.join("\n"))); + } + + self.transact(cx, |this, cx| { + this.buffer.update(cx, |buffer, cx| { + buffer.edit(edits, None, cx); + }); + + this.change_selections(Some(Autoscroll::fit()), cx, |s| { + s.select(contiguous_row_selections); + }); + + this.request_autoscroll(Autoscroll::fit(), cx); + }); + + // TODO: + // Write tests + // - Use cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six "); in tests + // Mikayla check for perf stuff + // Shuffle + } + pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = &display_map.buffer_snapshot; diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 247a7b021d77cac7d2386f67fc676d7723b037fc..18028eb8ee80d8fad2c937d73c2441dff3dd5150 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2500,6 +2500,85 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) { }); } +#[gpui::test] +fn test_sort_lines_with_single_selection(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + cx.add_window(|cx| { + let buffer = MultiBuffer::build_simple("dddd\nccc\nbb\na\n\n", cx); + let mut editor = build_editor(buffer.clone(), cx); + let buffer = buffer.read(cx).as_singleton().unwrap(); + + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(0, 2)..Point::new(0, 2)]) + }); + editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); + assert_eq!( + buffer.read(cx).text(), + "dddd\nccc\nbb\na\n\n", + "no sorting when single cursor parked on single line" + ); + assert_eq!( + editor.selections.ranges::(cx), + &[Point::new(0, 2)..Point::new(0, 2)] + ); + + editor.change_selections(None, cx, |s| { + s.select_ranges([Point::new(0, 2)..Point::new(5, 1)]) + }); + editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); + assert_eq!( + buffer.read(cx).text(), + "a\nbb\nccc\ndddd\n\n", + "single selection is sorted" + ); + assert_eq!( + editor.selections.ranges::(cx), + &[Point::new(0, 0)..Point::new(5, 1)] + ); + + editor + }); +} + +#[gpui::test] +fn test_sort_lines_with_multi_selection(cx: &mut TestAppContext) { + init_test(cx, |_| {}); + + cx.add_window(|cx| { + let buffer = MultiBuffer::build_simple("dddd\nccc\nbb\na\n\n3\n2\n1\n\n", cx); + let mut editor = build_editor(buffer.clone(), cx); + let buffer = buffer.read(cx).as_singleton().unwrap(); + + editor.change_selections(None, cx, |s| { + s.select_ranges([ + Point::new(0, 2)..Point::new(3, 2), + Point::new(5, 0)..Point::new(7, 1), + ]) + }); + + editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); + assert_eq!(buffer.read(cx).text(), "a\nbb\nccc\ndddd\n\n1\n2\n3\n\n"); + assert_eq!( + editor.selections.ranges::(cx), + &[Point::new(0, 5)..Point::new(2, 2)] + ); + assert_eq!( + editor.selections.ranges::(cx), + &[Point::new(0, 5)..Point::new(2, 2)] + ); + + // assert_eq!( + // editor.selections.ranges::(cx), + // [ + // Point::new(0, 7)..Point::new(0, 7), + // Point::new(1, 3)..Point::new(1, 3) + // ] + // ); + editor + }); +} + #[gpui::test] fn test_duplicate_line(cx: &mut TestAppContext) { init_test(cx, |_| {}); From 299818cde0afeb4a0d83311a238f8fea6f77f199 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 25 Jul 2023 11:44:13 -0400 Subject: [PATCH 2/5] Fix rand import and tweak callbacks Co-Authored-By: Julia <30666851+ForLoveOfCats@users.noreply.github.com> --- crates/editor/Cargo.toml | 3 +-- crates/editor/src/editor.rs | 53 ++++++++++--------------------------- 2 files changed, 15 insertions(+), 41 deletions(-) diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 087ce81c268ffcbc11f598031b22d9ee272a114e..bc1c904404972cb847947b8c60147bcf4a41186b 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -10,7 +10,6 @@ doctest = false [features] test-support = [ - "rand", "copilot/test-support", "text/test-support", "language/test-support", @@ -62,8 +61,8 @@ serde.workspace = true serde_derive.workspace = true smallvec.workspace = true smol.workspace = true +rand.workspace = true -rand = { workspace = true, optional = true } tree-sitter-rust = { workspace = true, optional = true } tree-sitter-html = { workspace = true, optional = true } tree-sitter-typescript = { workspace = true, optional = true } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index fe7667cd2badfef7010dbce7de70e1db5048b1ca..04821df62eb5433b11545be379fe9f9252d7ee29 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -74,8 +74,9 @@ pub use multi_buffer::{ }; use ordered_float::OrderedFloat; use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction}; -use rand::seq::SliceRandom; -use rand::thread_rng; +use rand::{seq::SliceRandom, thread_rng}; +// use rand::seq::SliceRandom; +// use rand::thread_rng; use scroll::{ autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, }; @@ -353,7 +354,7 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::sort_lines_case_sensitive); cx.add_action(Editor::sort_lines_case_insensitive); cx.add_action(Editor::reverse_lines); - // cx.add_action(Editor::shuffle_lines); + cx.add_action(Editor::shuffle_lines); cx.add_action(Editor::delete_to_previous_word_start); cx.add_action(Editor::delete_to_previous_subword_start); cx.add_action(Editor::delete_to_next_word_end); @@ -4220,12 +4221,7 @@ impl Editor { _: &SortLinesCaseSensitive, cx: &mut ViewContext, ) { - self.manipulate_lines(cx, { - |mut lines| { - lines.sort(); - lines - } - }) + self.manipulate_lines(cx, |lines| lines.sort()) } pub fn sort_lines_case_insensitive( @@ -4233,35 +4229,20 @@ impl Editor { _: &SortLinesCaseInsensitive, cx: &mut ViewContext, ) { - self.manipulate_lines(cx, { - |mut lines| { - lines.sort_by_key(|line| line.to_lowercase()); - lines - } - }) + self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase())) } pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, { - |mut lines| { - lines.reverse(); - lines - } - }) + self.manipulate_lines(cx, |lines| lines.reverse()) } - // pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { - // self.manipulate_lines(cx, { - // |mut lines| { - // lines.shuffle(&mut thread_rng()); - // lines - // } - // }) - // } + pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { + self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng())) + } fn manipulate_lines(&mut self, cx: &mut ViewContext, mut callback: Fn) where - Fn: FnMut(Vec<&str>) -> Vec<&str>, + Fn: FnMut(&mut Vec<&str>), { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); @@ -4280,12 +4261,12 @@ impl Editor { &display_map, &mut selections, ); + let start = Point::new(start_row, 0); let end = Point::new(end_row - 1, buffer.line_len(end_row - 1)); let text = buffer.text_for_range(start..end).collect::(); - // TODO SORT LINES: Is there a smarter / more effificent way to obtain lines? - let lines = text.split("\n").collect::>(); - let lines = callback(lines); + let mut lines = text.split("\n").collect::>(); + callback(&mut lines); edits.push((start..end, lines.join("\n"))); } @@ -4300,12 +4281,6 @@ impl Editor { this.request_autoscroll(Autoscroll::fit(), cx); }); - - // TODO: - // Write tests - // - Use cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six "); in tests - // Mikayla check for perf stuff - // Shuffle } pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext) { From 93ec73da2930daf4e4ceeb6b283787e0d2e1581e Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 25 Jul 2023 14:04:25 -0400 Subject: [PATCH 3/5] Fix code computing new selections Co-Authored-By: Mikayla Maki --- crates/editor/src/editor.rs | 55 ++++++++++++++++------ crates/editor/src/editor_tests.rs | 12 ++--- crates/editor/src/selections_collection.rs | 2 +- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 04821df62eb5433b11545be379fe9f9252d7ee29..655fb39257f791ba086fede8f4f60c2449f13b31 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -75,8 +75,6 @@ pub use multi_buffer::{ use ordered_float::OrderedFloat; use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction}; use rand::{seq::SliceRandom, thread_rng}; -// use rand::seq::SliceRandom; -// use rand::thread_rng; use scroll::{ autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, }; @@ -4221,7 +4219,7 @@ impl Editor { _: &SortLinesCaseSensitive, cx: &mut ViewContext, ) { - self.manipulate_lines(cx, |lines| lines.sort()) + self.manipulate_lines(cx, |text| text.sort()) } pub fn sort_lines_case_insensitive( @@ -4229,20 +4227,24 @@ impl Editor { _: &SortLinesCaseInsensitive, cx: &mut ViewContext, ) { - self.manipulate_lines(cx, |lines| lines.sort_by_key(|line| line.to_lowercase())) + self.manipulate_lines(cx, |text| text.sort_by_key(|line| line.to_lowercase())) } pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| lines.reverse()) + self.manipulate_lines(cx, |lines| { + lines.reverse(); + }) } pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng())) + self.manipulate_lines(cx, |lines| { + lines.shuffle(&mut thread_rng()); + }) } fn manipulate_lines(&mut self, cx: &mut ViewContext, mut callback: Fn) where - Fn: FnMut(&mut Vec<&str>), + Fn: FnMut(&mut [&str]), { let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); let buffer = self.buffer.read(cx).snapshot(cx); @@ -4252,9 +4254,9 @@ impl Editor { let selections = self.selections.all::(cx); let mut selections = selections.iter().peekable(); let mut contiguous_row_selections = Vec::new(); + let mut new_selections = Vec::new(); while let Some(selection) = selections.next() { - // Find all the selections that span a contiguous row range let (start_row, end_row) = consume_contiguous_rows( &mut contiguous_row_selections, selection, @@ -4262,12 +4264,35 @@ impl Editor { &mut selections, ); - let start = Point::new(start_row, 0); - let end = Point::new(end_row - 1, buffer.line_len(end_row - 1)); - let text = buffer.text_for_range(start..end).collect::(); - let mut lines = text.split("\n").collect::>(); - callback(&mut lines); - edits.push((start..end, lines.join("\n"))); + let start_point = Point::new(start_row, 0); + let end_point = Point::new(end_row - 1, buffer.line_len(end_row - 1)); + let text = buffer + .text_for_range(start_point..end_point) + .collect::(); + let mut text = text.split("\n").collect_vec(); + + let text_len = text.len(); + callback(&mut text); + + // This is a current limitation with selections. + // If we wanted to support removing or adding lines, we'd need to fix the logic associated with selections. + debug_assert!( + text.len() == text_len, + "callback should not change the number of lines" + ); + + edits.push((start_point..end_point, text.join("\n"))); + let start_anchor = buffer.anchor_after(start_point); + let end_anchor = buffer.anchor_before(end_point); + + // Make selection and push + new_selections.push(Selection { + id: selection.id, + start: start_anchor.to_offset(&buffer), + end: end_anchor.to_offset(&buffer), + goal: SelectionGoal::None, + reversed: selection.reversed, + }); } self.transact(cx, |this, cx| { @@ -4276,7 +4301,7 @@ impl Editor { }); this.change_selections(Some(Autoscroll::fit()), cx, |s| { - s.select(contiguous_row_selections); + s.select(new_selections); }); this.request_autoscroll(Autoscroll::fit(), cx); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 18028eb8ee80d8fad2c937d73c2441dff3dd5150..bc814f8c15e34b66546f0bcadea71c6c37773741 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2501,7 +2501,7 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) { } #[gpui::test] -fn test_sort_lines_with_single_selection(cx: &mut TestAppContext) { +fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) { init_test(cx, |_| {}); cx.add_window(|cx| { @@ -2510,7 +2510,7 @@ fn test_sort_lines_with_single_selection(cx: &mut TestAppContext) { let buffer = buffer.read(cx).as_singleton().unwrap(); editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(0, 2)..Point::new(0, 2)]) + s.select_ranges([Point::new(0, 1)..Point::new(0, 1)]) }); editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); assert_eq!( @@ -2520,13 +2520,13 @@ fn test_sort_lines_with_single_selection(cx: &mut TestAppContext) { ); assert_eq!( editor.selections.ranges::(cx), - &[Point::new(0, 2)..Point::new(0, 2)] + &[Point::new(0, 1)..Point::new(0, 2)] ); editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(0, 2)..Point::new(5, 1)]) + s.select_ranges([Point::new(0, 2)..Point::new(5, 0)]) }); - editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); + //editor.sort_lines(); assert_eq!( buffer.read(cx).text(), "a\nbb\nccc\ndddd\n\n", @@ -2542,7 +2542,7 @@ fn test_sort_lines_with_single_selection(cx: &mut TestAppContext) { } #[gpui::test] -fn test_sort_lines_with_multi_selection(cx: &mut TestAppContext) { +fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) { init_test(cx, |_| {}); cx.add_window(|cx| { diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index a22506f751b1ab36d518cdc13f60dc61dfecf6c7..1921bc0738ec588c8b9218c717de497232a345ec 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -138,7 +138,7 @@ impl SelectionsCollection { .collect() } - // Returns all of the selections, adjusted to take into account the selection line_mode + /// Returns all of the selections, adjusted to take into account the selection line_mode pub fn all_adjusted(&self, cx: &mut AppContext) -> Vec> { let mut selections = self.all::(cx); if self.line_mode { From bf2ca57f55182ab8568f4d6abe4d69bbc63c9f5b Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 25 Jul 2023 14:48:11 -0400 Subject: [PATCH 4/5] Remove { and } from single-line closure --- crates/editor/src/editor.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 655fb39257f791ba086fede8f4f60c2449f13b31..e05837740d4c50fbe7e02e51272bd6340884ec8a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4231,15 +4231,11 @@ impl Editor { } pub fn reverse_lines(&mut self, _: &ReverseLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| { - lines.reverse(); - }) + self.manipulate_lines(cx, |lines| lines.reverse()) } pub fn shuffle_lines(&mut self, _: &ShuffleLines, cx: &mut ViewContext) { - self.manipulate_lines(cx, |lines| { - lines.shuffle(&mut thread_rng()); - }) + self.manipulate_lines(cx, |lines| lines.shuffle(&mut thread_rng())) } fn manipulate_lines(&mut self, cx: &mut ViewContext, mut callback: Fn) From 4085df5146f015d88c0daca229896ec32e94ee84 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Tue, 25 Jul 2023 15:17:16 -0400 Subject: [PATCH 5/5] Add tests for manipulate_lines() --- crates/editor/src/editor_tests.rs | 199 ++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 64 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index bc814f8c15e34b66546f0bcadea71c6c37773741..eb03d2bdc0f103c2b94352312d3e86df860c821c 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2501,82 +2501,153 @@ fn test_join_lines_with_multi_selection(cx: &mut TestAppContext) { } #[gpui::test] -fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) { +async fn test_manipulate_lines_with_single_selection(cx: &mut TestAppContext) { init_test(cx, |_| {}); - cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("dddd\nccc\nbb\na\n\n", cx); - let mut editor = build_editor(buffer.clone(), cx); - let buffer = buffer.read(cx).as_singleton().unwrap(); + let mut cx = EditorTestContext::new(cx).await; - editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(0, 1)..Point::new(0, 1)]) - }); - editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); - assert_eq!( - buffer.read(cx).text(), - "dddd\nccc\nbb\na\n\n", - "no sorting when single cursor parked on single line" - ); - assert_eq!( - editor.selections.ranges::(cx), - &[Point::new(0, 1)..Point::new(0, 2)] - ); + // Test sort_lines_case_insensitive() + cx.set_state(indoc! {" + «z + y + x + Z + Y + Xˇ» + "}); + cx.update_editor(|e, cx| e.sort_lines_case_insensitive(&SortLinesCaseInsensitive, cx)); + cx.assert_editor_state(indoc! {" + «x + X + y + Y + z + Zˇ» + "}); - editor.change_selections(None, cx, |s| { - s.select_ranges([Point::new(0, 2)..Point::new(5, 0)]) - }); - //editor.sort_lines(); - assert_eq!( - buffer.read(cx).text(), - "a\nbb\nccc\ndddd\n\n", - "single selection is sorted" - ); - assert_eq!( - editor.selections.ranges::(cx), - &[Point::new(0, 0)..Point::new(5, 1)] - ); + // Test reverse_lines() + cx.set_state(indoc! {" + «5 + 4 + 3 + 2 + 1ˇ» + "}); + cx.update_editor(|e, cx| e.reverse_lines(&ReverseLines, cx)); + cx.assert_editor_state(indoc! {" + «1 + 2 + 3 + 4 + 5ˇ» + "}); - editor - }); + // Skip testing shuffle_line() + + // From here on out, test more complex cases of manipulate_lines() with a single driver method: sort_lines_case_sensitive() + // Since all methods calling manipulate_lines() are doing the exact same general thing (reordering lines) + + // Don't manipulate when cursor is on single line, but expand the selection + cx.set_state(indoc! {" + ddˇdd + ccc + bb + a + "}); + cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx)); + cx.assert_editor_state(indoc! {" + «ddddˇ» + ccc + bb + a + "}); + + // Basic manipulate case + // Start selection moves to column 0 + // End of selection shrinks to fit shorter line + cx.set_state(indoc! {" + dd«d + ccc + bb + aaaaaˇ» + "}); + cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx)); + cx.assert_editor_state(indoc! {" + «aaaaa + bb + ccc + dddˇ» + "}); + + // Manipulate case with newlines + cx.set_state(indoc! {" + dd«d + ccc + + bb + aaaaa + + ˇ» + "}); + cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx)); + cx.assert_editor_state(indoc! {" + « + + aaaaa + bb + ccc + dddˇ» + + "}); } #[gpui::test] -fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) { +async fn test_manipulate_lines_with_multi_selection(cx: &mut TestAppContext) { init_test(cx, |_| {}); - cx.add_window(|cx| { - let buffer = MultiBuffer::build_simple("dddd\nccc\nbb\na\n\n3\n2\n1\n\n", cx); - let mut editor = build_editor(buffer.clone(), cx); - let buffer = buffer.read(cx).as_singleton().unwrap(); - - editor.change_selections(None, cx, |s| { - s.select_ranges([ - Point::new(0, 2)..Point::new(3, 2), - Point::new(5, 0)..Point::new(7, 1), - ]) - }); + let mut cx = EditorTestContext::new(cx).await; - editor.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx); - assert_eq!(buffer.read(cx).text(), "a\nbb\nccc\ndddd\n\n1\n2\n3\n\n"); - assert_eq!( - editor.selections.ranges::(cx), - &[Point::new(0, 5)..Point::new(2, 2)] - ); - assert_eq!( - editor.selections.ranges::(cx), - &[Point::new(0, 5)..Point::new(2, 2)] - ); + // Manipulate with multiple selections on a single line + cx.set_state(indoc! {" + dd«dd + cˇ»c«c + bb + aaaˇ»aa + "}); + cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx)); + cx.assert_editor_state(indoc! {" + «aaaaa + bb + ccc + ddddˇ» + "}); - // assert_eq!( - // editor.selections.ranges::(cx), - // [ - // Point::new(0, 7)..Point::new(0, 7), - // Point::new(1, 3)..Point::new(1, 3) - // ] - // ); - editor - }); + // Manipulate with multiple disjoin selections + cx.set_state(indoc! {" + 5« + 4 + 3 + 2 + 1ˇ» + + dd«dd + ccc + bb + aaaˇ»aa + "}); + cx.update_editor(|e, cx| e.sort_lines_case_sensitive(&SortLinesCaseSensitive, cx)); + cx.assert_editor_state(indoc! {" + «1 + 2 + 3 + 4 + 5ˇ» + + «aaaaa + bb + ccc + ddddˇ» + "}); } #[gpui::test]