@@ -5,8 +5,10 @@ use std::cmp::Reverse;
55
66/// Convert annotated test string to test string and selection.
77///
8−/// `^` for `anchor` and `|` for head (`@` for primary), both must appear
9−/// or otherwise it will panic.
8+/// `#[|` for primary selection with head before anchor followed by `]#`.
9+/// `#(|` for secondary selection with head before anchor followed by `)#`.
10+/// `#[` for primary selection with head after anchor followed by `|]#`.
11+/// `#(` for secondary selection with head after anchor followed by `|)#`.
1012///
1113/// # Examples
1214///
@@ -15,7 +17,7 @@ use std::cmp::Reverse;
1517/// use smallvec::smallvec;
1618///
1719/// assert_eq!(
18−/// print("^a@b|c^"),
20+/// print("#[a|]#b#(|c)#"),
1921/// ("abc".to_owned(), Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0))
2022/// );
2123/// ```
@@ -26,56 +28,101 @@ use std::cmp::Reverse;
2628/// Panics when missing head or anchor.
2729/// Panics when head come after head or anchor come after anchor.
2830pub fn print(s: &str) -> (String, Selection) {
29− let mut anchor = None;
30− let mut head = None;
3131 let mut primary = None;
3232 let mut ranges = SmallVec::new();
33− let mut i = 0;
34− let s = s
35− .chars()
36− .filter(|c| {
37− match c {
38− '^' if anchor != None => panic!("anchor without head {s:?}"),
39− '^' if head == None => anchor = Some(i),
40− '^' => ranges.push(Range::new(i, head.take().unwrap())),
41− '|' if head != None => panic!("head without anchor {s:?}"),
42− '|' if anchor == None => head = Some(i),
43− '|' => ranges.push(Range::new(anchor.take().unwrap(), i)),
44− '@' if primary != None => panic!("head (primary) already appeared {s:?}"),
45− '@' if head != None => panic!("head (primary) without anchor {s:?}"),
46− '@' if anchor == None => {
47− primary = Some(ranges.len());
48− head = Some(i);
33+ let mut iter = s.chars().peekable();
34+ let mut left = String::with_capacity(s.len());
35+ 'outer: while let Some(c) = iter.next() {
36+ let start = left.len();
37+ if c == '#' {
38+ if iter.next_if_eq(&'[').is_some() {
39+ if primary.is_some() {
40+ panic!("primary `#[` already appeared {left:?} {s:?}");
4941 }
50− '@' => {
51− primary = Some(ranges.len());
52− ranges.push(Range::new(anchor.take().unwrap(), i));
42+ if iter.next_if_eq(&'|').is_some() {
43+ while let Some(c) = iter.next() {
44+ if c == ']' && iter.next_if_eq(&'#').is_some() {
45+ primary = Some(ranges.len());
46+ ranges.push(Range::new(left.len(), start));
47+ continue 'outer;
48+ } else {
49+ left.push(c);
50+ }
51+ }
52+ panic!("missing primary end `]#` {left:?} {s:?}");
53+ } else {
54+ while let Some(c) = iter.next() {
55+ if c == '|' {
56+ if let Some(cc) = iter.next_if_eq(&']') {
57+ if iter.next_if_eq(&'#').is_some() {
58+ primary = Some(ranges.len());
59+ ranges.push(Range::new(start, left.len()));
60+ continue 'outer;
61+ } else {
62+ left.push(c);
63+ left.push(cc);
64+ }
65+ } else {
66+ left.push(c);
67+ }
68+ } else {
69+ left.push(c);
70+ }
71+ }
72+ panic!("missing primary end `|]#` {left:?} {s:?}");
5373 }
54− _ => {
55− i += 1;
56− return true;
74+ } else if iter.next_if_eq(&'(').is_some() {
75+ if iter.next_if_eq(&'|').is_some() {
76+ while let Some(c) = iter.next() {
77+ if c == ')' && iter.next_if_eq(&'#').is_some() {
78+ ranges.push(Range::new(left.len(), start));
79+ continue 'outer;
80+ } else {
81+ left.push(c);
82+ }
83+ }
84+ panic!("missing end `)#` {left:?} {s:?}");
85+ } else {
86+ while let Some(c) = iter.next() {
87+ if c == '|' {
88+ if let Some(cc) = iter.next_if_eq(&')') {
89+ if iter.next_if_eq(&'#').is_some() {
90+ ranges.push(Range::new(start, left.len()));
91+ continue 'outer;
92+ } else {
93+ left.push(c);
94+ left.push(cc);
95+ }
96+ } else {
97+ left.push(c);
98+ }
99+ } else {
100+ left.push(c);
101+ }
102+ }
103+ panic!("missing end `|)#` {left:?} {s:?}");
57104 }
58− };
59− false
60− })
61− .collect();
62− if head.is_some() {
63− panic!("missing anchor (|) {s:?}");
64− }
65− if anchor.is_some() {
66− panic!("missing head (^) {s:?}");
105+ } else {
106+ left.push(c);
107+ }
108+ } else {
109+ left.push(c);
110+ }
67111 }
68112 let primary = match primary {
69113 Some(i) => i,
70− None => panic!("missing primary (@) {s:?}"),
114+ None => panic!("missing primary `#[|]#` {s:?}"),
71115 };
72116 let selection = Selection::new(ranges, primary);
73− (s, selection)
117+ (left, selection)
74118}
75119
76120/// Convert test string and selection to annotated test string.
77121///
78−/// `^` for `anchor` and `|` for head (`@` for primary).
122+/// `#[|` for primary selection with head before anchor followed by `]#`.
123+/// `#(|` for secondary selection with head before anchor followed by `)#`.
124+/// `#[` for primary selection with head after anchor followed by `|]#`.
125+/// `#(` for secondary selection with head after anchor followed by `|)#`.
79126///
80127/// # Examples
81128///
@@ -85,28 +132,30 @@ pub fn print(s: &str) -> (String, Selection) {
85132///
86133/// assert_eq!(
87134/// plain("abc", Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)),
88−/// "^a@b|c^".to_owned()
135+/// "#[a|]#b#(|c)#".to_owned()
89136/// );
90137/// ```
91138pub fn plain(s: &str, selection: Selection) -> String {
92139 let primary = selection.primary_index();
93− let mut out = String::with_capacity(s.len() + 2 * selection.len());
140+ let mut out = String::with_capacity(s.len() + 5 * selection.len());
94141 out.push_str(s);
95142 let mut insertion: Vec<_> = selection
96143 .iter()
97144 .enumerate()
98145 .flat_map(|(i, range)| {
99− [
100− // sort like this before reversed so anchor < head later
101− (range.head, if i == primary { '@' } else { '|' }),
102− (range.anchor, '^'),
103− ]
146+ // sort like this before reversed so anchor < head later
147+ match (range.anchor < range.head, i == primary) {
148+ (true, true) => [(range.anchor, "#["), (range.head, "|]#")],
149+ (true, false) => [(range.anchor, "#("), (range.head, "|)#")],
150+ (false, true) => [(range.anchor, "]#"), (range.head, "#[|")],
151+ (false, false) => [(range.anchor, ")#"), (range.head, "#(|")],
152+ }
104153 })
105154 .collect();
106155 // insert in reverse order
107156 insertion.sort_unstable_by_key(|k| Reverse(k.0));
108− for (i, c) in insertion {
109− out.insert(i, c);
157+ for (i, s) in insertion {
158+ out.insert_str(i, s);
110159 }
111160 out
112161}