on master help sign in
Allow explicit newlines in test DSL

The current test DSL currently has no way to express being at the end of a line, save for putting an explicit LF or CRLF inside the `#[|]#`. The problem with this approach is that it can add unintended extra new lines if used in conjunction with raw strings, which insert newlines for you. This is a simple attempt to mitigate this problem. If there is an explicit newline character at the end of the selection, and then it is immediately followed by the same newline character at the right end of the selection, this following newline is removed. This way, one can express a cursor at the end of a line explicitly.

commit58ea1930544df165a4a1342932e4dd8cb93e14ea authorSkyler Hawthorne <skyler@dead10ck.com> dateSat 17 Sep 2022, 09:34 (4 years ago) parenta264faa
6 files changed+106−71
Cargo.lock+30−23 helix-core/Cargo.toml+1 helix-core/src/movement.rs+7−7 helix-core/src/test.rs+43−16 helix-core/src/textobject.rs+3−3 helix-term/tests/test/commands.rs+22−22
Cargo.lock+30−23
@@ -143,9 +143,9 @@ dependencies = [
143143
144144[[package]]
145145name = "chrono"
146−version = "0.4.24"
146+version = "0.4.23"
147147source = "registry+https://github.com/rust-lang/crates.io-index"
148−checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
148+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
149149dependencies = [
150150 "iana-time-zone",
151151 "num-integer",
@@ -932,9 +932,9 @@ dependencies = [
932932
933933[[package]]
934934name = "gix-tempfile"
935−version = "5.0.0"
935+version = "5.0.1"
936936source = "registry+https://github.com/rust-lang/crates.io-index"
937−checksum = "743bae41b5db7f085dc7acc54ed72c98853a6e5dabb355e95caa7b534f21b35c"
937+checksum = "aed73ef9642f779d609fd19acc332ac1597b978ee87ec11743a68eefaed65bfa"
938938dependencies = [
939939 "libc",
940940 "once_cell",
@@ -1081,6 +1081,7 @@ dependencies = [
10811081 "hashbrown 0.13.2",
10821082 "helix-loader",
10831083 "imara-diff",
1084+ "indoc 1.0.9",
10841085 "log",
10851086 "once_cell",
10861087 "quickcheck",
@@ -1176,7 +1177,7 @@ dependencies = [
11761177 "helix-vcs",
11771178 "helix-view",
11781179 "ignore",
1179− "indoc",
1180+ "indoc 2.0.1",
11801181 "libc",
11811182 "log",
11821183 "once_cell",
@@ -1349,6 +1350,12 @@ dependencies = [
13491350 "hashbrown 0.12.3",
13501351]
13511352
1353+[[package]]
1354+name = "indoc"
1355+version = "1.0.9"
1356+source = "registry+https://github.com/rust-lang/crates.io-index"
1357+checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
1358+
13521359[[package]]
13531360name = "indoc"
13541361version = "2.0.1"
@@ -1532,6 +1539,15 @@ dependencies = [
15321539 "minimal-lexical",
15331540]
15341541
1542+[[package]]
1543+name = "nom8"
1544+version = "0.2.0"
1545+source = "registry+https://github.com/rust-lang/crates.io-index"
1546+checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
1547+dependencies = [
1548+ "memchr",
1549+]
1550+
15351551[[package]]
15361552name = "num-integer"
15371553version = "0.1.45"
@@ -1775,18 +1791,18 @@ checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
17751791
17761792[[package]]
17771793name = "serde"
1778−version = "1.0.155"
1794+version = "1.0.152"
17791795source = "registry+https://github.com/rust-lang/crates.io-index"
1780−checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8"
1796+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
17811797dependencies = [
17821798 "serde_derive",
17831799]
17841800
17851801[[package]]
17861802name = "serde_derive"
1787−version = "1.0.155"
1803+version = "1.0.152"
17881804source = "registry+https://github.com/rust-lang/crates.io-index"
1789−checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630"
1805+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
17901806dependencies = [
17911807 "proc-macro2",
17921808 "quote",
@@ -2120,9 +2136,9 @@ dependencies = [
21202136
21212137[[package]]
21222138name = "toml"
2123−version = "0.7.3"
2139+version = "0.7.2"
21242140source = "registry+https://github.com/rust-lang/crates.io-index"
2125−checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
2141+checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
21262142dependencies = [
21272143 "serde",
21282144 "serde_spanned",
@@ -2141,15 +2157,15 @@ dependencies = [
21412157
21422158[[package]]
21432159name = "toml_edit"
2144−version = "0.19.6"
2160+version = "0.19.3"
21452161source = "registry+https://github.com/rust-lang/crates.io-index"
2146−checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae"
2162+checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
21472163dependencies = [
21482164 "indexmap",
2165+ "nom8",
21492166 "serde",
21502167 "serde_spanned",
21512168 "toml_datetime",
2152− "winnow",
21532169]
21542170
21552171[[package]]
@@ -2452,15 +2468,6 @@ version = "0.42.1"
24522468source = "registry+https://github.com/rust-lang/crates.io-index"
24532469checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
24542470
2455−[[package]]
2456−name = "winnow"
2457−version = "0.3.5"
2458−source = "registry+https://github.com/rust-lang/crates.io-index"
2459−checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
2460−dependencies = [
2461− "memchr",
2462−]
2463−
24642471[[package]]
24652472name = "xtask"
24662473version = "0.6.0"
helix-core/Cargo.toml+1
@@ -49,3 +49,4 @@ textwrap = "0.16.0"
4949
5050[dev-dependencies]
5151quickcheck = { version = "1", default-features = false }
52+indoc = "1.0.6"
helix-core/src/movement.rs+7−7
@@ -1474,7 +1474,7 @@ mod test {
14741474 let text = Rope::from(s.as_str());
14751475 let selection =
14761476 selection.transform(|r| move_prev_paragraph(text.slice(..), r, 1, Movement::Move));
1477− let actual = crate::test::plain(&s, selection);
1477+ let actual = crate::test::plain(&s, &selection);
14781478 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
14791479 }
14801480 }
@@ -1497,7 +1497,7 @@ mod test {
14971497 let text = Rope::from(s.as_str());
14981498 let selection =
14991499 selection.transform(|r| move_prev_paragraph(text.slice(..), r, 2, Movement::Move));
1500− let actual = crate::test::plain(&s, selection);
1500+ let actual = crate::test::plain(&s, &selection);
15011501 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
15021502 }
15031503 }
@@ -1520,7 +1520,7 @@ mod test {
15201520 let text = Rope::from(s.as_str());
15211521 let selection = selection
15221522 .transform(|r| move_prev_paragraph(text.slice(..), r, 1, Movement::Extend));
1523− let actual = crate::test::plain(&s, selection);
1523+ let actual = crate::test::plain(&s, &selection);
15241524 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
15251525 }
15261526 }
@@ -1540,7 +1540,7 @@ mod test {
15401540 "a\nb\n\n#[goto\nthird\n\n|]#paragraph",
15411541 ),
15421542 (
1543− "a\nb#[\n|]#\ngoto\nsecond\n\nparagraph",
1543+ "a\nb#[\n|]#\n\ngoto\nsecond\n\nparagraph",
15441544 "a\nb#[\n\n|]#goto\nsecond\n\nparagraph",
15451545 ),
15461546 (
@@ -1562,7 +1562,7 @@ mod test {
15621562 let text = Rope::from(s.as_str());
15631563 let selection =
15641564 selection.transform(|r| move_next_paragraph(text.slice(..), r, 1, Movement::Move));
1565− let actual = crate::test::plain(&s, selection);
1565+ let actual = crate::test::plain(&s, &selection);
15661566 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
15671567 }
15681568 }
@@ -1585,7 +1585,7 @@ mod test {
15851585 let text = Rope::from(s.as_str());
15861586 let selection =
15871587 selection.transform(|r| move_next_paragraph(text.slice(..), r, 2, Movement::Move));
1588− let actual = crate::test::plain(&s, selection);
1588+ let actual = crate::test::plain(&s, &selection);
15891589 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
15901590 }
15911591 }
@@ -1608,7 +1608,7 @@ mod test {
16081608 let text = Rope::from(s.as_str());
16091609 let selection = selection
16101610 .transform(|r| move_next_paragraph(text.slice(..), r, 1, Movement::Extend));
1611− let actual = crate::test::plain(&s, selection);
1611+ let actual = crate::test::plain(&s, &selection);
16121612 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
16131613 }
16141614 }
helix-core/src/test.rs+43−16
@@ -2,6 +2,7 @@
22use crate::{Range, Selection};
33use smallvec::SmallVec;
44use std::cmp::Reverse;
5+use unicode_segmentation::UnicodeSegmentation;
56
67/// Convert annotated test string to test string and selection.
78///
@@ -10,6 +11,10 @@ use std::cmp::Reverse;
1011/// `#[` for primary selection with head after anchor followed by `|]#`.
1112/// `#(` for secondary selection with head after anchor followed by `|)#`.
1213///
14+/// If the selection contains any LF or CRLF sequences, which are immediately
15+/// followed by the same grapheme, then the subsequent one is removed. This is
16+/// to allow representing having the cursor over the end of the line.
17+///
1318/// # Examples
1419///
1520/// ```
@@ -30,23 +35,23 @@ use std::cmp::Reverse;
3035pub fn print(s: &str) -> (String, Selection) {
3136 let mut primary_idx = None;
3237 let mut ranges = SmallVec::new();
33− let mut iter = s.chars().peekable();
38+ let mut iter = UnicodeSegmentation::graphemes(s, true).peekable();
3439 let mut left = String::with_capacity(s.len());
3540
3641 'outer: while let Some(c) = iter.next() {
3742 let start = left.chars().count();
3843
39− if c != '#' {
40− left.push(c);
44+ if c != "#" {
45+ left.push_str(c);
4146 continue;
4247 }
4348
4449 let (is_primary, close_pair) = match iter.next() {
45− Some('[') => (true, ']'),
46− Some('(') => (false, ')'),
50+ Some("[") => (true, "]"),
51+ Some("(") => (false, ")"),
4752 Some(ch) => {
4853 left.push('#');
49− left.push(ch);
54+ left.push_str(ch);
5055 continue;
5156 }
5257 None => break,
@@ -56,24 +61,45 @@ pub fn print(s: &str) -> (String, Selection) {
5661 panic!("primary `#[` already appeared {:?} {:?}", left, s);
5762 }
5863
59− let head_at_beg = iter.next_if_eq(&'|').is_some();
64+ let head_at_beg = iter.next_if_eq(&"|").is_some();
65+ let last_grapheme = |s: &str| {
66+ UnicodeSegmentation::graphemes(s, true)
67+ .last()
68+ .map(String::from)
69+ };
6070
6171 while let Some(c) = iter.next() {
62− if !(c == close_pair && iter.peek() == Some(&'#')) {
63− left.push(c);
72+ let next = iter.peek();
73+ let mut prev = last_grapheme(left.as_str());
74+
75+ if !(c == close_pair && next == Some(&"#")) {
76+ left.push_str(c);
6477 continue;
6578 }
6679
6780 if !head_at_beg {
68− let prev = left.pop().unwrap();
69− if prev != '|' {
70− left.push(prev);
71− left.push(c);
72− continue;
81+ match &prev {
82+ Some(p) if p != "|" => {
83+ left.push_str(c);
84+ continue;
85+ }
86+ Some(p) if p == "|" => {
87+ left.pop().unwrap(); // pop the |
88+ prev = last_grapheme(left.as_str());
89+ }
90+ _ => (),
7391 }
7492 }
7593
7694 iter.next(); // skip "#"
95+ let next = iter.peek();
96+
97+ // skip explicit line end inside selection
98+ if (prev == Some(String::from("\r\n")) || prev == Some(String::from("\n")))
99+ && next.map(|n| String::from(*n)) == prev
100+ {
101+ iter.next();
102+ }
77103
78104 if is_primary {
79105 primary_idx = Some(ranges.len());
@@ -118,11 +144,11 @@ pub fn print(s: &str) -> (String, Selection) {
118144/// use smallvec::smallvec;
119145///
120146/// assert_eq!(
121−/// plain("abc", Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)),
147+/// plain("abc", &Selection::new(smallvec![Range::new(0, 1), Range::new(3, 2)], 0)),
122148/// "#[a|]#b#(|c)#".to_owned()
123149/// );
124150/// ```
125−pub fn plain(s: &str, selection: Selection) -> String {
151+pub fn plain(s: &str, selection: &Selection) -> String {
126152 let primary = selection.primary_index();
127153 let mut out = String::with_capacity(s.len() + 5 * selection.len());
128154 out.push_str(s);
@@ -147,6 +173,7 @@ pub fn plain(s: &str, selection: Selection) -> String {
147173 out
148174}
149175
176+#[allow(clippy::module_inception)]
150177#[cfg(test)]
151178#[allow(clippy::module_inception)]
152179mod test {
helix-core/src/textobject.rs+3−3
@@ -437,7 +437,7 @@ mod test {
437437 let text = Rope::from(s.as_str());
438438 let selection = selection
439439 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Inside, 1));
440− let actual = crate::test::plain(&s, selection);
440+ let actual = crate::test::plain(&s, &selection);
441441 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
442442 }
443443 }
@@ -460,7 +460,7 @@ mod test {
460460 let text = Rope::from(s.as_str());
461461 let selection = selection
462462 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Inside, 2));
463− let actual = crate::test::plain(&s, selection);
463+ let actual = crate::test::plain(&s, &selection);
464464 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
465465 }
466466 }
@@ -491,7 +491,7 @@ mod test {
491491 let text = Rope::from(s.as_str());
492492 let selection = selection
493493 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Around, 1));
494− let actual = crate::test::plain(&s, selection);
494+ let actual = crate::test::plain(&s, &selection);
495495 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
496496 }
497497 }
helix-term/tests/test/commands.rs+22−22
@@ -201,12 +201,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
201201 .as_str(),
202202 "|echo foo<ret>",
203203 platform_line(indoc! {"\
204− #[|foo
205− ]#
206− #(|foo
207− )#
208− #(|foo
209− )#
204+ #[|foo\n]#
205+
206+ #(|foo\n)#
207+
208+ #(|foo\n)#
209+
210210 "})
211211 .as_str(),
212212 ))
@@ -222,12 +222,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
222222 .as_str(),
223223 "!echo foo<ret>",
224224 platform_line(indoc! {"\
225− #[|foo
226− ]#lorem
227− #(|foo
228− )#ipsum
229− #(|foo
230− )#dolor
225+ #[|foo\n]#
226+ lorem
227+ #(|foo\n)#
228+ ipsum
229+ #(|foo\n)#
230+ dolor
231231 "})
232232 .as_str(),
233233 ))
@@ -243,12 +243,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
243243 .as_str(),
244244 "<A-!>echo foo<ret>",
245245 platform_line(indoc! {"\
246− lorem#[|foo
247− ]#
248− ipsum#(|foo
249− )#
250− dolor#(|foo
251− )#
246+ lorem#[|foo\n]#
247+
248+ ipsum#(|foo\n)#
249+
250+ dolor#(|foo\n)#
251+
252252 "})
253253 .as_str(),
254254 ))
@@ -300,8 +300,8 @@ async fn test_extend_line() -> anyhow::Result<()> {
300300 platform_line(indoc! {"\
301301 #[lorem
302302 ipsum
303− dolor
304− |]#
303+ dolor\n|]#
304+
305305 "})
306306 .as_str(),
307307 ))
@@ -318,8 +318,8 @@ async fn test_extend_line() -> anyhow::Result<()> {
318318 "2x",
319319 platform_line(indoc! {"\
320320 #[lorem
321− ipsum
322− |]#
321+ ipsum\n|]#
322+
323323 "})
324324 .as_str(),
325325 ))