on master help sign in
Change test mark from ^@ to #[|]#
45b76dbIvan Tham
helix-core/src/movement.rs+60−27
@@ -1255,15 +1255,24 @@ mod test {
1255 #[test]
1255 #[test]
1256 fn test_behaviour_when_moving_to_prev_paragraph_single() {
1256 fn test_behaviour_when_moving_to_prev_paragraph_single() {
1257 let tests = [
1257 let tests = [
1258− ("^@", "^@"),
1258+ ("#[|]#", "#[|]#"),
1259− ("^s@tart at\nfirst char\n", "@s^tart at\nfirst char\n"),
1259+ ("#[s|]#tart at\nfirst char\n", "#[|s]#tart at\nfirst char\n"),
1260− ("start at\nlast char^\n@", "@start at\nlast char\n^"),
1260+ ("start at\nlast char#[\n|]#", "#[|start at\nlast char\n]#"),
1261− ("goto\nfirst\n\n^p@aragraph", "@goto\nfirst\n\n^paragraph"),
1262− ("goto\nfirst\n^\n@paragraph", "@goto\nfirst\n\n^paragraph"),
1263− ("goto\nsecond\n\np^a@ragraph", "goto\nsecond\n\n@pa^ragraph"),
1264 (
1261 (
1262+ "goto\nfirst\n\n#[p|]#aragraph",
1263+ "#[|goto\nfirst\n\n]#paragraph",
1264+ ),
1265+ (
1266+ "goto\nfirst\n#[\n|]#paragraph",
1267+ "#[|goto\nfirst\n\n]#paragraph",
1268+ ),
1269+ (
1270+ "goto\nsecond\n\np#[a|]#ragraph",
1271+ "goto\nsecond\n\n#[|pa]#ragraph",
1272+ ),
1273+ (
1265− "here\n\nhave\nmultiple\nparagraph\n\n\n\n\n^@",
1274+ "here\n\nhave\nmultiple\nparagraph\n\n\n\n\n#[|]#",
1266− "here\n\n@have\nmultiple\nparagraph\n\n\n\n\n^",
1275+ "here\n\n#[|have\nmultiple\nparagraph\n\n\n\n\n]#",
1267 ),
1276 ),
1268 ];
1277 ];
1269
1278
@@ -1280,8 +1289,14 @@ mod test {
1280 #[test]
1289 #[test]
1281 fn test_behaviour_when_moving_to_prev_paragraph_double() {
1290 fn test_behaviour_when_moving_to_prev_paragraph_double() {
1282 let tests = [
1291 let tests = [
1292+ (
1283− ("on^e@\n\ntwo\n\nthree\n\n", "@one^\n\ntwo\n\nthree\n\n"),
1293+ "on#[e|]#\n\ntwo\n\nthree\n\n",
1294+ "#[|one]#\n\ntwo\n\nthree\n\n",
1295+ ),
1296+ (
1284− ("one\n\ntwo\n\nth^r@ee\n\n", "one\n\n@two\n\nthr^ee\n\n"),
1297+ "one\n\ntwo\n\nth#[r|]#ee\n\n",
1298+ "one\n\n#[|two\n\nthr]#ee\n\n",
1299+ ),
1285 ];
1300 ];
1286
1301
1287 for (before, expected) in tests {
1302 for (before, expected) in tests {
@@ -1297,8 +1312,14 @@ mod test {
1297 #[test]
1312 #[test]
1298 fn test_behaviour_when_moving_to_prev_paragraph_extend() {
1313 fn test_behaviour_when_moving_to_prev_paragraph_extend() {
1299 let tests = [
1314 let tests = [
1315+ (
1300− ("one\n\n@two\n\n^three\n\n", "@one\n\ntwo\n\n^three\n\n"),
1316+ "one\n\n#[|two\n\n]#three\n\n",
1317+ "#[|one\n\ntwo\n\n]#three\n\n",
1318+ ),
1319+ (
1301− ("@one\n\ntwo\n\n^three\n\n", "@one\n\ntwo\n\n^three\n\n"),
1320+ "#[|one\n\ntwo\n\n]#three\n\n",
1321+ "#[|one\n\ntwo\n\n]#three\n\n",
1322+ ),
1302 ];
1323 ];
1303
1324
1304 for (before, expected) in tests {
1325 for (before, expected) in tests {
@@ -1314,24 +1335,24 @@ mod test {
1314 #[test]
1335 #[test]
1315 fn test_behaviour_when_moving_to_next_paragraph_single() {
1336 fn test_behaviour_when_moving_to_next_paragraph_single() {
1316 let tests = [
1337 let tests = [
1317− ("^@", "^@"),
1338+ ("#[|]#", "#[|]#"),
1318− ("^s@tart at\nfirst char\n", "^start at\nfirst char\n@"),
1339+ ("#[s|]#tart at\nfirst char\n", "#[start at\nfirst char\n|]#"),
1319− ("start at\nlast char^\n@", "start at\nlast char^\n@"),
1340+ ("start at\nlast char#[\n|]#", "start at\nlast char#[\n|]#"),
1320 (
1341 (
1321− "a\nb\n\n^g@oto\nthird\n\nparagraph",
1342+ "a\nb\n\n#[g|]#oto\nthird\n\nparagraph",
1322− "a\nb\n\n^goto\nthird\n\n@paragraph",
1343+ "a\nb\n\n#[goto\nthird\n\n|]#paragraph",
1323 ),
1344 ),
1324 (
1345 (
1325− "a\nb\n^\n@goto\nthird\n\nparagraph",
1346+ "a\nb\n#[\n|]#goto\nthird\n\nparagraph",
1326− "a\nb\n\n^goto\nthird\n\n@paragraph",
1347+ "a\nb\n\n#[goto\nthird\n\n|]#paragraph",
1327 ),
1348 ),
1328 (
1349 (
1329− "a\nb^\n@\ngoto\nsecond\n\nparagraph",
1350+ "a\nb#[\n|]#\ngoto\nsecond\n\nparagraph",
1330− "a\nb^\n\n@goto\nsecond\n\nparagraph",
1351+ "a\nb#[\n\n|]#goto\nsecond\n\nparagraph",
1331 ),
1352 ),
1332 (
1353 (
1333− "here\n\nhave\n^m@ultiple\nparagraph\n\n\n\n\n",
1354+ "here\n\nhave\n#[m|]#ultiple\nparagraph\n\n\n\n\n",
1334− "here\n\nhave\n^multiple\nparagraph\n\n\n\n\n@",
1355+ "here\n\nhave\n#[multiple\nparagraph\n\n\n\n\n|]#",
1335 ),
1356 ),
1336 ];
1357 ];
1337
1358
@@ -1348,8 +1369,14 @@ mod test {
1348 #[test]
1369 #[test]
1349 fn test_behaviour_when_moving_to_next_paragraph_double() {
1370 fn test_behaviour_when_moving_to_next_paragraph_double() {
1350 let tests = [
1371 let tests = [
1372+ (
1351− ("one\n\ntwo\n\nth^r@ee\n\n", "one\n\ntwo\n\nth^ree\n\n@"),
1373+ "one\n\ntwo\n\nth#[r|]#ee\n\n",
1374+ "one\n\ntwo\n\nth#[ree\n\n|]#",
1375+ ),
1376+ (
1352− ("on^e@\n\ntwo\n\nthree\n\n", "on^e\n\ntwo\n\n@three\n\n"),
1377+ "on#[e|]#\n\ntwo\n\nthree\n\n",
1378+ "on#[e\n\ntwo\n\n|]#three\n\n",
1379+ ),
1353 ];
1380 ];
1354
1381
1355 for (before, expected) in tests {
1382 for (before, expected) in tests {
@@ -1365,8 +1392,14 @@ mod test {
1365 #[test]
1392 #[test]
1366 fn test_behaviour_when_moving_to_next_paragraph_extend() {
1393 fn test_behaviour_when_moving_to_next_paragraph_extend() {
1367 let tests = [
1394 let tests = [
1395+ (
1368− ("one\n\n^two\n\n@three\n\n", "one\n\n^two\n\nthree\n\n@"),
1396+ "one\n\n#[two\n\n|]#three\n\n",
1397+ "one\n\n#[two\n\nthree\n\n|]#",
1398+ ),
1399+ (
1369− ("one\n\n^two\n\nthree\n\n@", "one\n\n^two\n\nthree\n\n@"),
1400+ "one\n\n#[two\n\nthree\n\n|]#",
1401+ "one\n\n#[two\n\nthree\n\n|]#",
1402+ ),
1370 ];
1403 ];
1371
1404
1372 for (before, expected) in tests {
1405 for (before, expected) in tests {
helix-core/src/test.rs+97−48
@@ -5,8 +5,10 @@ use std::cmp::Reverse;
5
5
6/// Convert annotated test string to test string and selection.
6/// Convert annotated test string to test string and selection.
7///
7///
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 `|)#`.
10///
12///
11/// # Examples
13/// # Examples
12///
14///
@@ -15,7 +17,7 @@ use std::cmp::Reverse;
15/// use smallvec::smallvec;
17/// use smallvec::smallvec;
16///
18///
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))
20/// );
22/// );
21/// ```
23/// ```
@@ -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;
30− let mut head = 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());
48− head = Some(i);
49 }
41 }
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));
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() {
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()));
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:?}");
53 }
73 }
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));
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:?}");
57 }
104 }
58− };
105+ } else {
59− false
106+ left.push(c);
60− })
107+ }
61− .collect();
108+ } else {
62− if head.is_some() {
109+ left.push(c);
63− panic!("missing anchor (|) {s:?}");
110+ }
64− }
65− if anchor.is_some() {
66− panic!("missing head (^) {s:?}");
67 }
111 }
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:?}"),
71 };
115 };
72 let selection = Selection::new(ranges, primary);
116 let selection = Selection::new(ranges, primary);
73− (s, selection)
117+ (left, selection)
74}
118}
75
119
76/// Convert test string and selection to annotated test string.
120/// Convert test string and selection to annotated test string.
77///
121///
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 `|)#`.
79///
126///
80/// # Examples
127/// # Examples
81///
128///
@@ -85,28 +132,30 @@ pub fn print(s: &str) -> (String, Selection) {
85///
132///
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()
89/// );
136/// );
90/// ```
137/// ```
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
96 .iter()
143 .iter()
97 .enumerate()
144 .enumerate()
98 .flat_map(|(i, range)| {
145 .flat_map(|(i, range)| {
99− [
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, "#(|")],
103− ]
152+ }
104 })
153 })
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);
110 }
159 }
111 out
160 out
112}
161}
helix-core/src/textobject.rs+28−22
@@ -357,18 +357,21 @@ mod test {
357 #[test]
357 #[test]
358 fn test_textobject_paragraph_inside_single() {
358 fn test_textobject_paragraph_inside_single() {
359 let tests = [
359 let tests = [
360− ("^@", "^@"),
360+ ("#[|]#", "#[|]#"),
361− ("firs^t@\n\nparagraph\n\n", "^first\n@\nparagraph\n\n"),
361+ ("firs#[t|]#\n\nparagraph\n\n", "#[first\n|]#\nparagraph\n\n"),
362− ("second\n\npa^r@agraph\n\n", "second\n\n^paragraph\n@\n"),
363− ("^f@irst char\n\n", "^first char\n@\n"),
364− ("last char\n^\n@", "last char\n\n^@"),
365 (
362 (
366− "empty to line\n^\n@paragraph boundary\n\n",
363+ "second\n\npa#[r|]#agraph\n\n",
367− "empty to line\n\n^paragraph boundary\n@\n",
364+ "second\n\n#[paragraph\n|]#\n",
368 ),
365 ),
366+ ("#[f|]#irst char\n\n", "#[first char\n|]#\n"),
367+ ("last char\n#[\n|]#", "last char\n\n#[|]#"),
369 (
368 (
369+ "empty to line\n#[\n|]#paragraph boundary\n\n",
370+ "empty to line\n\n#[paragraph boundary\n|]#\n",
371+ ),
372+ (
370− "line to empty\n\n^p@aragraph boundary\n\n",
373+ "line to empty\n\n#[p|]#aragraph boundary\n\n",
371− "line to empty\n\n^paragraph boundary\n@\n",
374+ "line to empty\n\n#[paragraph boundary\n|]#\n",
372 ),
375 ),
373 ];
376 ];
374
377
@@ -386,12 +389,12 @@ mod test {
386 fn test_textobject_paragraph_inside_double() {
389 fn test_textobject_paragraph_inside_double() {
387 let tests = [
390 let tests = [
388 (
391 (
389− "last two\n\n^p@aragraph\n\nwithout whitespaces\n\n",
392+ "last two\n\n#[p|]#aragraph\n\nwithout whitespaces\n\n",
390− "last two\n\n^paragraph\n\nwithout whitespaces\n@\n",
393+ "last two\n\n#[paragraph\n\nwithout whitespaces\n|]#\n",
391 ),
394 ),
392 (
395 (
393− "last two\n^\n@paragraph\n\nwithout whitespaces\n\n",
396+ "last two\n#[\n|]#paragraph\n\nwithout whitespaces\n\n",
394− "last two\n\n^paragraph\n\nwithout whitespaces\n@\n",
397+ "last two\n\n#[paragraph\n\nwithout whitespaces\n|]#\n",
395 ),
398 ),
396 ];
399 ];
397
400
@@ -408,18 +411,21 @@ mod test {
408 #[test]
411 #[test]
409 fn test_textobject_paragraph_around_single() {
412 fn test_textobject_paragraph_around_single() {
410 let tests = [
413 let tests = [
411− ("^@", "^@"),
414+ ("#[|]#", "#[|]#"),
412− ("firs^t@\n\nparagraph\n\n", "^first\n\n@paragraph\n\n"),
415+ ("firs#[t|]#\n\nparagraph\n\n", "#[first\n\n|]#paragraph\n\n"),
416+ (
413− ("second\n\npa^r@agraph\n\n", "second\n\n^paragraph\n\n@"),
417+ "second\n\npa#[r|]#agraph\n\n",
418+ "second\n\n#[paragraph\n\n|]#",
419+ ),
414− ("^f@irst char\n\n", "^first char\n\n@"),
420+ ("#[f|]#irst char\n\n", "#[first char\n\n|]#"),
415− ("last char\n^\n@", "last char\n\n^@"),
421+ ("last char\n#[\n|]#", "last char\n\n#[|]#"),
416 (
422 (
417− "empty to line\n^\n@paragraph boundary\n\n",
423+ "empty to line\n#[\n|]#paragraph boundary\n\n",
418− "empty to line\n\n^paragraph boundary\n\n@",
424+ "empty to line\n\n#[paragraph boundary\n\n|]#",
419 ),
425 ),
420 (
426 (
421− "line to empty\n\n^p@aragraph boundary\n\n",
427+ "line to empty\n\n#[p|]#aragraph boundary\n\n",
422− "line to empty\n\n^paragraph boundary\n\n@",
428+ "line to empty\n\n#[paragraph boundary\n\n|]#",
423 ),
429 ),
424 ];
430 ];
425
431