on master help sign in
Selection: fail early if new() is called with no ranges.
798dbd2Blaž Hrastnik
helix-core/src/selection.rs+11−2
@@ -126,6 +126,7 @@ impl Range {
126}
126}
127
127
128/// A selection consists of one or more selection ranges.
128/// A selection consists of one or more selection ranges.
129+/// invariant: A selection can never be empty (always contains at least primary range).
129#[derive(Debug, Clone)]
130#[derive(Debug, Clone)]
130pub struct Selection {
131pub struct Selection {
131 ranges: SmallVec<[Range; 1]>,
132 ranges: SmallVec<[Range; 1]>,
@@ -205,12 +206,14 @@ impl Selection {
205 // TODO: consume an iterator or a vec to reduce allocations?
206 // TODO: consume an iterator or a vec to reduce allocations?
206 #[must_use]
207 #[must_use]
207 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
208 pub fn new(ranges: SmallVec<[Range; 1]>, primary_index: usize) -> Self {
209+ assert!(!ranges.is_empty());
210+
208 fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Selection {
211 fn normalize(mut ranges: SmallVec<[Range; 1]>, mut primary_index: usize) -> Selection {
209 let primary = ranges[primary_index];
212 let primary = ranges[primary_index];
210 ranges.sort_unstable_by_key(Range::from);
213 ranges.sort_unstable_by_key(Range::from);
211 primary_index = ranges.iter().position(|&range| range == primary).unwrap();
214 primary_index = ranges.iter().position(|&range| range == primary).unwrap();
212
215
213− let mut result: SmallVec<[Range; 1]> = SmallVec::new();
216+ let mut result = SmallVec::new();
214
217
215 // TODO: we could do with one vec by removing elements as we mutate
218 // TODO: we could do with one vec by removing elements as we mutate
216
219
@@ -294,7 +297,7 @@ impl<'a> IntoIterator for &'a Selection {
294 }
297 }
295}
298}
296
299
297−// TODO: checkSelection -> check if valid for doc length
300+// TODO: checkSelection -> check if valid for doc length && sorted
298
301
299pub fn keep_matches(
302pub fn keep_matches(
300 text: RopeSlice,
303 text: RopeSlice,
@@ -387,6 +390,12 @@ pub fn split_on_matches(
387mod test {
390mod test {
388 use super::*;
391 use super::*;
389
392
393+ #[test]
394+ #[should_panic]
395+ fn test_new_empty() {
396+ let sel = Selection::new(smallvec![], 0);
397+ }
398+
390 #[test]
399 #[test]
391 fn test_create_normalizes_and_merges() {
400 fn test_create_normalizes_and_merges() {
392 let sel = Selection::new(
401 let sel = Selection::new(
helix-core/src/transaction.rs+3−1
@@ -464,11 +464,13 @@ impl Transaction {
464 I: IntoIterator<Item = Change> + ExactSizeIterator,
464 I: IntoIterator<Item = Change> + ExactSizeIterator,
465 {
465 {
466 let len = doc.len_chars();
466 let len = doc.len_chars();
467− let acc = Vec::with_capacity(2 * changes.len() + 1);
467+ let acc = Vec::with_capacity(2 * changes.len() + 1); // rough estimate
468 let mut changeset = ChangeSet { changes: acc, len };
468 let mut changeset = ChangeSet { changes: acc, len };
469
469
470 // TODO: verify ranges are ordered and not overlapping or change will panic.
470 // TODO: verify ranges are ordered and not overlapping or change will panic.
471
471
472+ // TODO: test for (pos, pos, None) to factor out as nothing
473+
472 let mut last = 0;
474 let mut last = 0;
473 for (from, to, tendril) in changes {
475 for (from, to, tendril) in changes {
474 // Retain from last "to" to current "from"
476 // Retain from last "to" to current "from"