mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 09:26:19 +04:00
Add command to merge consecutive ranges in selection (#5047)
This commit is contained in:
parent
1b89d3e535
commit
1107296ca9
@ -111,6 +111,7 @@ ### Selection manipulation
|
||||
| `s` | Select all regex matches inside selections | `select_regex` |
|
||||
| `S` | Split selection into subselections on regex matches | `split_selection` |
|
||||
| `Alt-s` | Split selection on newlines | `split_selection_on_newline` |
|
||||
| `Alt-_ ` | Merge consecutive selections | `merge_consecutive_selections` |
|
||||
| `&` | Align selection in columns | `align_selections` |
|
||||
| `_` | Trim whitespace from the selection | `trim_selections` |
|
||||
| `;` | Collapse selection onto a single cursor | `collapse_selection` |
|
||||
|
@ -495,28 +495,53 @@ pub fn point(pos: usize) -> Self {
|
||||
|
||||
/// Normalizes a `Selection`.
|
||||
fn normalize(mut self) -> Self {
|
||||
let primary = self.ranges[self.primary_index];
|
||||
let mut primary = self.ranges[self.primary_index];
|
||||
self.ranges.sort_unstable_by_key(Range::from);
|
||||
|
||||
self.ranges.dedup_by(|curr_range, prev_range| {
|
||||
if prev_range.overlaps(curr_range) {
|
||||
let new_range = curr_range.merge(*prev_range);
|
||||
if prev_range == &primary || curr_range == &primary {
|
||||
primary = new_range;
|
||||
}
|
||||
*prev_range = new_range;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
self.primary_index = self
|
||||
.ranges
|
||||
.iter()
|
||||
.position(|&range| range == primary)
|
||||
.unwrap();
|
||||
|
||||
let mut prev_i = 0;
|
||||
for i in 1..self.ranges.len() {
|
||||
if self.ranges[prev_i].overlaps(&self.ranges[i]) {
|
||||
self.ranges[prev_i] = self.ranges[prev_i].merge(self.ranges[i]);
|
||||
} else {
|
||||
prev_i += 1;
|
||||
self.ranges[prev_i] = self.ranges[i];
|
||||
}
|
||||
if i == self.primary_index {
|
||||
self.primary_index = prev_i;
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
self.ranges.truncate(prev_i + 1);
|
||||
// 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() {
|
||||
let new_range = curr_range.merge(*prev_range);
|
||||
if prev_range == &primary || curr_range == &primary {
|
||||
primary = new_range;
|
||||
}
|
||||
*prev_range = new_range;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
self.primary_index = self
|
||||
.ranges
|
||||
.iter()
|
||||
.position(|&range| range == primary)
|
||||
.unwrap();
|
||||
|
||||
self
|
||||
}
|
||||
@ -1132,6 +1157,52 @@ fn test_split_on_matches() {
|
||||
&["", "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 {
|
||||
|
@ -244,6 +244,7 @@ pub fn doc(&self) -> &str {
|
||||
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_selections, "Merge consecutive selections",
|
||||
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_selections(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,
|
||||
|
@ -76,6 +76,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
|
||||
|
||||
"s" => select_regex,
|
||||
"A-s" => split_selection_on_newline,
|
||||
"A-_" => merge_consecutive_selections,
|
||||
"S" => split_selection,
|
||||
";" => collapse_selection,
|
||||
"A-;" => flip_selections,
|
||||
|
Loading…
Reference in New Issue
Block a user