diff options
Diffstat (limited to 'helix-core/src/test.rs')
-rw-r--r-- | helix-core/src/test.rs | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/helix-core/src/test.rs b/helix-core/src/test.rs new file mode 100644 index 00000000..983c9a57 --- /dev/null +++ b/helix-core/src/test.rs @@ -0,0 +1,111 @@ +//! Test helpers. +use crate::{Range, Selection}; +use smallvec::SmallVec; +use std::cmp::Reverse; + +/// Convert annotated test string to test string and selection. +/// +/// `^` for `anchor` and `|` for head (`@` for primary), both must appear +/// or otherwise it will panic. +/// +/// # Examples +/// +/// ``` +/// use helix_core::{Range, Selection, test::print}; +/// use smallvec::smallvec; +/// +/// assert_eq!( +/// print("^a@b|c^"), +/// ("abc".to_owned(), Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)) +/// ); +/// ``` +/// +/// # Panics +/// +/// Panics when missing primary or appeared more than once. +/// Panics when missing head or anchor. +/// Panics when head come after head or anchor come after anchor. +pub fn print(s: &str) -> (String, Selection) { + let mut anchor = None; + let mut head = None; + let mut primary = None; + let mut ranges = SmallVec::new(); + let mut i = 0; + let s = s + .chars() + .filter(|c| { + match c { + '^' if anchor != None => panic!("anchor without head {s:?}"), + '^' if head == None => anchor = Some(i), + '^' => ranges.push(Range::new(i, head.take().unwrap())), + '|' if head != None => panic!("head without anchor {s:?}"), + '|' if anchor == None => head = Some(i), + '|' => ranges.push(Range::new(anchor.take().unwrap(), i)), + '@' if primary != None => panic!("head (primary) already appeared {s:?}"), + '@' if head != None => panic!("head (primary) without anchor {s:?}"), + '@' if anchor == None => { + primary = Some(ranges.len()); + head = Some(i); + } + '@' => { + primary = Some(ranges.len()); + ranges.push(Range::new(anchor.take().unwrap(), i)); + } + _ => { + i += 1; + return true; + } + }; + false + }) + .collect(); + if head.is_some() { + panic!("missing anchor (|) {s:?}"); + } + if anchor.is_some() { + panic!("missing head (^) {s:?}"); + } + let primary = match primary { + Some(i) => i, + None => panic!("missing primary (@) {s:?}"), + }; + let selection = Selection::new(ranges, primary); + (s, selection) +} + +/// Convert test string and selection to annotated test string. +/// +/// `^` for `anchor` and `|` for head (`@` for primary). +/// +/// # Examples +/// +/// ``` +/// use helix_core::{Range, Selection, test::plain}; +/// use smallvec::smallvec; +/// +/// assert_eq!( +/// plain("abc", Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)), +/// "^a@b|c^".to_owned() +/// ); +/// ``` +pub fn plain(s: &str, selection: Selection) -> String { + let primary = selection.primary_index(); + let mut out = String::with_capacity(s.len() + 2 * selection.len()); + out.push_str(s); + let mut insertion: Vec<_> = selection + .iter() + .enumerate() + .flat_map(|(i, range)| { + [ + (range.anchor, '^'), + (range.head, if i == primary { '@' } else { '|' }), + ] + }) + .collect(); + // insert in reverse order + insertion.sort_unstable_by_key(|k| Reverse(k.0)); + for (i, c) in insertion { + out.insert(i, c); + } + out +} |