You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helix-plus/helix-core/src/test.rs

162 lines
6.1 KiB
Rust

//! Test helpers.
use crate::{Range, Selection};
use smallvec::SmallVec;
use std::cmp::Reverse;
/// Convert annotated test string to test string and selection.
///
/// `#[|` for primary selection with head before anchor followed by `]#`.
/// `#(|` for secondary selection with head before anchor followed by `)#`.
/// `#[` for primary selection with head after anchor followed by `|]#`.
/// `#(` for secondary selection with head after anchor followed by `|)#`.
///
/// # 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 primary = None;
let mut ranges = SmallVec::new();
let mut iter = s.chars().peekable();
let mut left = String::with_capacity(s.len());
'outer: while let Some(c) = iter.next() {
let start = left.len();
if c == '#' {
if iter.next_if_eq(&'[').is_some() {
if primary.is_some() {
panic!("primary `#[` already appeared {left:?} {s:?}");
}
if iter.next_if_eq(&'|').is_some() {
while let Some(c) = iter.next() {
if c == ']' && iter.next_if_eq(&'#').is_some() {
primary = Some(ranges.len());
ranges.push(Range::new(left.len(), start));
continue 'outer;
} else {
left.push(c);
}
}
panic!("missing primary end `]#` {left:?} {s:?}");
} else {
while let Some(c) = iter.next() {
if c == '|' {
if let Some(cc) = iter.next_if_eq(&']') {
if iter.next_if_eq(&'#').is_some() {
primary = Some(ranges.len());
ranges.push(Range::new(start, left.len()));
continue 'outer;
} else {
left.push(c);
left.push(cc);
}
} else {
left.push(c);
}
} else {
left.push(c);
}
}
panic!("missing primary end `|]#` {left:?} {s:?}");
}
} else if iter.next_if_eq(&'(').is_some() {
if iter.next_if_eq(&'|').is_some() {
while let Some(c) = iter.next() {
if c == ')' && iter.next_if_eq(&'#').is_some() {
ranges.push(Range::new(left.len(), start));
continue 'outer;
} else {
left.push(c);
}
}
panic!("missing end `)#` {left:?} {s:?}");
} else {
while let Some(c) = iter.next() {
if c == '|' {
if let Some(cc) = iter.next_if_eq(&')') {
if iter.next_if_eq(&'#').is_some() {
ranges.push(Range::new(start, left.len()));
continue 'outer;
} else {
left.push(c);
left.push(cc);
}
} else {
left.push(c);
}
} else {
left.push(c);
}
}
panic!("missing end `|)#` {left:?} {s:?}");
}
} else {
left.push(c);
}
} else {
left.push(c);
}
}
let primary = match primary {
Some(i) => i,
None => panic!("missing primary `#[|]#` {s:?}"),
};
let selection = Selection::new(ranges, primary);
(left, selection)
}
/// Convert test string and selection to annotated test string.
///
/// `#[|` for primary selection with head before anchor followed by `]#`.
/// `#(|` for secondary selection with head before anchor followed by `)#`.
/// `#[` for primary selection with head after anchor followed by `|]#`.
/// `#(` for secondary selection with head after anchor followed by `|)#`.
///
/// # 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() + 5 * selection.len());
out.push_str(s);
let mut insertion: Vec<_> = selection
.iter()
.enumerate()
.flat_map(|(i, range)| {
// sort like this before reversed so anchor < head later
match (range.anchor < range.head, i == primary) {
(true, true) => [(range.anchor, "#["), (range.head, "|]#")],
(true, false) => [(range.anchor, "#("), (range.head, "|)#")],
(false, true) => [(range.anchor, "]#"), (range.head, "#[|")],
(false, false) => [(range.anchor, ")#"), (range.head, "#(|")],
}
})
.collect();
// insert in reverse order
insertion.sort_unstable_by_key(|k| Reverse(k.0));
for (i, s) in insertion {
out.insert_str(i, s);
}
out
}