From a0b7fd7a878d5fb33a74cba7216ee74aa2520f13 Mon Sep 17 00:00:00 2001 From: Dylan Bulfin Date: Sun, 4 Dec 2022 18:50:23 -0500 Subject: [PATCH] Added command Rewrote command, added test Moved keymap Refactoring command, added test coverage Added primary index calculation Moving primary_index calculation Refactored code to use fold instead of loop Changed to use dedup_by Removed duplicate merge call Fix primary index calculation --- helix-core/src/selection.rs | 71 ++++++++++++++++++++++++++++++++ helix-term/src/commands.rs | 7 ++++ helix-term/src/keymap/default.rs | 1 + 3 files changed, 79 insertions(+) diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index 1f28ecefb750..f6db2bfff91a 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -521,6 +521,31 @@ impl Selection { self } + // Merges all ranges that are consecutive + pub fn merge_consecutive_ranges(mut self) -> Self { + let mut primary = self.ranges[self.primary_index]; + + self.ranges.dedup_by(|curr_range, prev_range| { + if prev_range.to() == curr_range.from() { + if prev_range == &primary || curr_range == &primary { + primary = curr_range.merge(*prev_range); + } + *prev_range = curr_range.merge(*prev_range); + true + } else { + false + } + }); + + self.primary_index = self + .ranges + .iter() + .position(|&range| range == primary) + .unwrap(); + + self + } + // TODO: consume an iterator or a vec to reduce allocations? #[must_use] pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self { @@ -1132,6 +1157,52 @@ mod test { &["", "abcd", "efg", "rs", "xyz"] ); } + + #[test] + fn test_merge_consecutive_ranges() { + let selection = Selection::new( + smallvec![ + Range::new(0, 1), + Range::new(1, 10), + Range::new(15, 20), + Range::new(25, 26), + Range::new(26, 30) + ], + 4, + ); + + let result = selection.merge_consecutive_ranges(); + + assert_eq!( + result.ranges(), + &[Range::new(0, 10), Range::new(15, 20), Range::new(25, 30)] + ); + assert_eq!(result.primary_index, 2); + + let selection = Selection::new(smallvec![Range::new(0, 1)], 0); + let result = selection.merge_consecutive_ranges(); + + assert_eq!(result.ranges(), &[Range::new(0, 1)]); + assert_eq!(result.primary_index, 0); + + let selection = Selection::new( + smallvec![ + Range::new(0, 1), + Range::new(1, 5), + Range::new(5, 8), + Range::new(8, 10), + Range::new(10, 15), + Range::new(18, 25) + ], + 3, + ); + + let result = selection.merge_consecutive_ranges(); + + assert_eq!(result.ranges(), &[Range::new(0, 15), Range::new(18, 25)]); + assert_eq!(result.primary_index, 0); + } + #[test] fn test_selection_contains() { fn contains(a: Vec<(usize, usize)>, b: Vec<(usize, usize)>) -> bool { diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 1310417e47be..3c5c48841388 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -244,6 +244,7 @@ impl MappableCommand { select_regex, "Select all regex matches inside selections", split_selection, "Split selections on regex matches", split_selection_on_newline, "Split selection on newlines", + merge_consecutive_ranges, "Merge consecutive ranges", search, "Search for regex pattern", rsearch, "Reverse search for regex pattern", search_next, "Select next search match", @@ -1589,6 +1590,12 @@ fn split_selection_on_newline(cx: &mut Context) { doc.set_selection(view.id, selection); } +fn merge_consecutive_ranges(cx: &mut Context) { + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id).clone().merge_consecutive_ranges(); + doc.set_selection(view.id, selection); +} + #[allow(clippy::too_many_arguments)] fn search_impl( editor: &mut Editor, diff --git a/helix-term/src/keymap/default.rs b/helix-term/src/keymap/default.rs index ebcd125aa129..f581304471a6 100644 --- a/helix-term/src/keymap/default.rs +++ b/helix-term/src/keymap/default.rs @@ -76,6 +76,7 @@ pub fn default() -> HashMap { "s" => select_regex, "A-s" => split_selection_on_newline, + "A-_" => merge_consecutive_ranges, "S" => split_selection, ";" => collapse_selection, "A-;" => flip_selections,