aboutsummaryrefslogtreecommitdiff
path: root/helix-core/src/test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core/src/test.rs')
-rw-r--r--helix-core/src/test.rs111
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
+}