@@ -5,8 +5,10 @@ use std::cmp::Reverse;
6/// Convert annotated test string to test string and selection.
6/// Convert annotated test string to test string and selection.
8−/// `^` for `anchor` and `|` for head (`@` for primary), both must appear
8+/// `#[|` for primary selection with head before anchor followed by `]#`.
9−/// or otherwise it will panic.
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 `|)#`.
11/// # Examples
13/// # Examples
@@ -15,7 +17,7 @@ use std::cmp::Reverse;
15/// use smallvec::smallvec;
17/// use smallvec::smallvec;
17/// assert_eq!(
19/// assert_eq!(
18−/// print("^a@b|c^"),
20+/// print("#[a|]#b#(|c)#"),
19/// ("abc".to_owned(), Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0))
21/// ("abc".to_owned(), Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0))
@@ -26,56 +28,101 @@ use std::cmp::Reverse;
26/// Panics when missing head or anchor.
28/// Panics when missing head or anchor.
27/// Panics when head come after head or anchor come after anchor.
29/// Panics when head come after head or anchor come after anchor.
28pub fn print(s: &str) -> (String, Selection) {
30pub fn print(s: &str) -> (String, Selection) {
29− let mut anchor = None;
31 let mut primary = None;
31 let mut primary = None;
32 let mut ranges = SmallVec::new();
32 let mut ranges = SmallVec::new();
33− let mut i = 0;
33+ let mut iter = s.chars().peekable();
34− let s = s
34+ let mut left = String::with_capacity(s.len());
35− .chars()
35+ 'outer: while let Some(c) = iter.next() {
36− .filter(|c| {
36+ let start = left.len();
37− match c {
37+ if c == '#' {
38− '^' if anchor != None => panic!("anchor without head {s:?}"),
38+ if iter.next_if_eq(&'[').is_some() {
39− '^' if head == None => anchor = Some(i),
39+ if primary.is_some() {
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:?}"),
40+ panic!("primary `#[` already appeared {left:?} {s:?}");
45− '@' if head != None => panic!("head (primary) without anchor {s:?}"),
46− '@' if anchor == None => {
47− primary = Some(ranges.len());
50− '@' => {
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));
52+ panic!("missing primary end `]#` {left:?} {s:?}");
54+ while let Some(c) = iter.next() {
56+ if let Some(cc) = iter.next_if_eq(&']') {
57+ if iter.next_if_eq(&'#').is_some() {
51− primary = Some(ranges.len());
58+ primary = Some(ranges.len());
52− ranges.push(Range::new(anchor.take().unwrap(), i));
59+ ranges.push(Range::new(start, left.len()));
72+ panic!("missing primary end `|]#` {left:?} {s:?}");
54− _ => {
74+ } else if iter.next_if_eq(&'(').is_some() {
55− i += 1;
75+ if iter.next_if_eq(&'|').is_some() {
56− return true;
76+ while let Some(c) = iter.next() {
77+ if c == ')' && iter.next_if_eq(&'#').is_some() {
78+ ranges.push(Range::new(left.len(), start));
84+ panic!("missing end `)#` {left:?} {s:?}");
86+ while let Some(c) = iter.next() {
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()));
103+ panic!("missing end `|)#` {left:?} {s:?}");
59− false
106+ left.push(c);
61− .collect();
108+ } else {
62− if head.is_some() {
109+ left.push(c);
63− panic!("missing anchor (|) {s:?}");
110+ }
65− if anchor.is_some() {
66− panic!("missing head (^) {s:?}");
68 let primary = match primary {
112 let primary = match primary {
69 Some(i) => i,
113 Some(i) => i,
70− None => panic!("missing primary (@) {s:?}"),
114+ None => panic!("missing primary `#[|]#` {s:?}"),
72 let selection = Selection::new(ranges, primary);
116 let selection = Selection::new(ranges, primary);
73− (s, selection)
117+ (left, selection)
76/// Convert test string and selection to annotated test string.
120/// Convert test string and selection to annotated test string.
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 `|)#`.
80/// # Examples
127/// # Examples
@@ -85,28 +132,30 @@ pub fn print(s: &str) -> (String, Selection) {
86/// assert_eq!(
133/// assert_eq!(
87/// plain("abc", Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)),
134/// 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()
91pub fn plain(s: &str, selection: Selection) -> String {
138pub fn plain(s: &str, selection: Selection) -> String {
92 let primary = selection.primary_index();
139 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());
94 out.push_str(s);
141 out.push_str(s);
95 let mut insertion: Vec<_> = selection
142 let mut insertion: Vec<_> = selection
97 .enumerate()
144 .enumerate()
98 .flat_map(|(i, range)| {
145 .flat_map(|(i, range)| {
100− // sort like this before reversed so anchor < head later
146+ // sort like this before reversed so anchor < head later
101− (range.head, if i == primary { '@' } else { '|' }),
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, "#[|")],
102− (range.anchor, '^'),
151+ (false, false) => [(range.anchor, ")#"), (range.head, "#(|")],
105 .collect();
154 .collect();
106 // insert in reverse order
155 // insert in reverse order
107 insertion.sort_unstable_by_key(|k| Reverse(k.0));
156 insertion.sort_unstable_by_key(|k| Reverse(k.0));
108− for (i, c) in insertion {
157+ for (i, s) in insertion {
109− out.insert(i, c);
158+ out.insert_str(i, s);