on master help sign in
Allow explicit newlines in test DSL
58ea193Skyler Hawthorne
Cargo.lock+30−23
@@ -143,9 +143,9 @@ dependencies = [
143
143
144[[package]]
144[[package]]
145name = "chrono"
145name = "chrono"
146−version = "0.4.24"
146+version = "0.4.23"
147source = "registry+https://github.com/rust-lang/crates.io-index"
147source = "registry+https://github.com/rust-lang/crates.io-index"
148−checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
148+checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
149dependencies = [
149dependencies = [
150 "iana-time-zone",
150 "iana-time-zone",
151 "num-integer",
151 "num-integer",
@@ -932,9 +932,9 @@ dependencies = [
932
932
933[[package]]
933[[package]]
934name = "gix-tempfile"
934name = "gix-tempfile"
935−version = "5.0.0"
935+version = "5.0.1"
936source = "registry+https://github.com/rust-lang/crates.io-index"
936source = "registry+https://github.com/rust-lang/crates.io-index"
937−checksum = "743bae41b5db7f085dc7acc54ed72c98853a6e5dabb355e95caa7b534f21b35c"
937+checksum = "aed73ef9642f779d609fd19acc332ac1597b978ee87ec11743a68eefaed65bfa"
938dependencies = [
938dependencies = [
939 "libc",
939 "libc",
940 "once_cell",
940 "once_cell",
@@ -1081,6 +1081,7 @@ dependencies = [
1081 "hashbrown 0.13.2",
1081 "hashbrown 0.13.2",
1082 "helix-loader",
1082 "helix-loader",
1083 "imara-diff",
1083 "imara-diff",
1084+ "indoc 1.0.9",
1084 "log",
1085 "log",
1085 "once_cell",
1086 "once_cell",
1086 "quickcheck",
1087 "quickcheck",
@@ -1176,7 +1177,7 @@ dependencies = [
1176 "helix-vcs",
1177 "helix-vcs",
1177 "helix-view",
1178 "helix-view",
1178 "ignore",
1179 "ignore",
1179− "indoc",
1180+ "indoc 2.0.1",
1180 "libc",
1181 "libc",
1181 "log",
1182 "log",
1182 "once_cell",
1183 "once_cell",
@@ -1349,6 +1350,12 @@ dependencies = [
1349 "hashbrown 0.12.3",
1350 "hashbrown 0.12.3",
1350]
1351]
1351
1352
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+
1352[[package]]
1359[[package]]
1353name = "indoc"
1360name = "indoc"
1354version = "2.0.1"
1361version = "2.0.1"
@@ -1532,6 +1539,15 @@ dependencies = [
1532 "minimal-lexical",
1539 "minimal-lexical",
1533]
1540]
1534
1541
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+
1535[[package]]
1551[[package]]
1536name = "num-integer"
1552name = "num-integer"
1537version = "0.1.45"
1553version = "0.1.45"
@@ -1775,18 +1791,18 @@ checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
1775
1791
1776[[package]]
1792[[package]]
1777name = "serde"
1793name = "serde"
1778−version = "1.0.155"
1794+version = "1.0.152"
1779source = "registry+https://github.com/rust-lang/crates.io-index"
1795source = "registry+https://github.com/rust-lang/crates.io-index"
1780−checksum = "71f2b4817415c6d4210bfe1c7bfcf4801b2d904cb4d0e1a8fdb651013c9e86b8"
1796+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
1781dependencies = [
1797dependencies = [
1782 "serde_derive",
1798 "serde_derive",
1783]
1799]
1784
1800
1785[[package]]
1801[[package]]
1786name = "serde_derive"
1802name = "serde_derive"
1787−version = "1.0.155"
1803+version = "1.0.152"
1788source = "registry+https://github.com/rust-lang/crates.io-index"
1804source = "registry+https://github.com/rust-lang/crates.io-index"
1789−checksum = "d071a94a3fac4aff69d023a7f411e33f40f3483f8c5190b1953822b6b76d7630"
1805+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
1790dependencies = [
1806dependencies = [
1791 "proc-macro2",
1807 "proc-macro2",
1792 "quote",
1808 "quote",
@@ -2120,9 +2136,9 @@ dependencies = [
2120
2136
2121[[package]]
2137[[package]]
2122name = "toml"
2138name = "toml"
2123−version = "0.7.3"
2139+version = "0.7.2"
2124source = "registry+https://github.com/rust-lang/crates.io-index"
2140source = "registry+https://github.com/rust-lang/crates.io-index"
2125−checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21"
2141+checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
2126dependencies = [
2142dependencies = [
2127 "serde",
2143 "serde",
2128 "serde_spanned",
2144 "serde_spanned",
@@ -2141,15 +2157,15 @@ dependencies = [
2141
2157
2142[[package]]
2158[[package]]
2143name = "toml_edit"
2159name = "toml_edit"
2144−version = "0.19.6"
2160+version = "0.19.3"
2145source = "registry+https://github.com/rust-lang/crates.io-index"
2161source = "registry+https://github.com/rust-lang/crates.io-index"
2146−checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae"
2162+checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
2147dependencies = [
2163dependencies = [
2148 "indexmap",
2164 "indexmap",
2165+ "nom8",
2149 "serde",
2166 "serde",
2150 "serde_spanned",
2167 "serde_spanned",
2151 "toml_datetime",
2168 "toml_datetime",
2152− "winnow",
2153]
2169]
2154
2170
2155[[package]]
2171[[package]]
@@ -2452,15 +2468,6 @@ version = "0.42.1"
2452source = "registry+https://github.com/rust-lang/crates.io-index"
2468source = "registry+https://github.com/rust-lang/crates.io-index"
2453checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
2469checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
2454
2470
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−
2464[[package]]
2471[[package]]
2465name = "xtask"
2472name = "xtask"
2466version = "0.6.0"
2473version = "0.6.0"
helix-core/Cargo.toml+1
@@ -49,3 +49,4 @@ textwrap = "0.16.0"
49
49
50[dev-dependencies]
50[dev-dependencies]
51quickcheck = { version = "1", default-features = false }
51quickcheck = { version = "1", default-features = false }
52+indoc = "1.0.6"
helix-core/src/movement.rs+7−7
@@ -1474,7 +1474,7 @@ mod test {
1474 let text = Rope::from(s.as_str());
1474 let text = Rope::from(s.as_str());
1475 let selection =
1475 let selection =
1476 selection.transform(|r| move_prev_paragraph(text.slice(..), r, 1, Movement::Move));
1476 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);
1478 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1478 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1479 }
1479 }
1480 }
1480 }
@@ -1497,7 +1497,7 @@ mod test {
1497 let text = Rope::from(s.as_str());
1497 let text = Rope::from(s.as_str());
1498 let selection =
1498 let selection =
1499 selection.transform(|r| move_prev_paragraph(text.slice(..), r, 2, Movement::Move));
1499 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);
1501 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1501 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1502 }
1502 }
1503 }
1503 }
@@ -1520,7 +1520,7 @@ mod test {
1520 let text = Rope::from(s.as_str());
1520 let text = Rope::from(s.as_str());
1521 let selection = selection
1521 let selection = selection
1522 .transform(|r| move_prev_paragraph(text.slice(..), r, 1, Movement::Extend));
1522 .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);
1524 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1524 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1525 }
1525 }
1526 }
1526 }
@@ -1540,7 +1540,7 @@ mod test {
1540 "a\nb\n\n#[goto\nthird\n\n|]#paragraph",
1540 "a\nb\n\n#[goto\nthird\n\n|]#paragraph",
1541 ),
1541 ),
1542 (
1542 (
1543− "a\nb#[\n|]#\ngoto\nsecond\n\nparagraph",
1543+ "a\nb#[\n|]#\n\ngoto\nsecond\n\nparagraph",
1544 "a\nb#[\n\n|]#goto\nsecond\n\nparagraph",
1544 "a\nb#[\n\n|]#goto\nsecond\n\nparagraph",
1545 ),
1545 ),
1546 (
1546 (
@@ -1562,7 +1562,7 @@ mod test {
1562 let text = Rope::from(s.as_str());
1562 let text = Rope::from(s.as_str());
1563 let selection =
1563 let selection =
1564 selection.transform(|r| move_next_paragraph(text.slice(..), r, 1, Movement::Move));
1564 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);
1566 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1566 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1567 }
1567 }
1568 }
1568 }
@@ -1585,7 +1585,7 @@ mod test {
1585 let text = Rope::from(s.as_str());
1585 let text = Rope::from(s.as_str());
1586 let selection =
1586 let selection =
1587 selection.transform(|r| move_next_paragraph(text.slice(..), r, 2, Movement::Move));
1587 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);
1589 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1589 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1590 }
1590 }
1591 }
1591 }
@@ -1608,7 +1608,7 @@ mod test {
1608 let text = Rope::from(s.as_str());
1608 let text = Rope::from(s.as_str());
1609 let selection = selection
1609 let selection = selection
1610 .transform(|r| move_next_paragraph(text.slice(..), r, 1, Movement::Extend));
1610 .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);
1612 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1612 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
1613 }
1613 }
1614 }
1614 }
helix-core/src/test.rs+43−16
@@ -2,6 +2,7 @@
2use crate::{Range, Selection};
2use crate::{Range, Selection};
3use smallvec::SmallVec;
3use smallvec::SmallVec;
4use std::cmp::Reverse;
4use std::cmp::Reverse;
5+use unicode_segmentation::UnicodeSegmentation;
5
6
6/// Convert annotated test string to test string and selection.
7/// Convert annotated test string to test string and selection.
7///
8///
@@ -10,6 +11,10 @@ use std::cmp::Reverse;
10/// `#[` for primary selection with head after anchor followed by `|]#`.
11/// `#[` for primary selection with head after anchor followed by `|]#`.
11/// `#(` for secondary selection with head after anchor followed by `|)#`.
12/// `#(` for secondary selection with head after anchor followed by `|)#`.
12///
13///
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+///
13/// # Examples
18/// # Examples
14///
19///
15/// ```
20/// ```
@@ -30,23 +35,23 @@ use std::cmp::Reverse;
30pub fn print(s: &str) -> (String, Selection) {
35pub fn print(s: &str) -> (String, Selection) {
31 let mut primary_idx = None;
36 let mut primary_idx = None;
32 let mut ranges = SmallVec::new();
37 let mut ranges = SmallVec::new();
33− let mut iter = s.chars().peekable();
38+ let mut iter = UnicodeSegmentation::graphemes(s, true).peekable();
34 let mut left = String::with_capacity(s.len());
39 let mut left = String::with_capacity(s.len());
35
40
36 'outer: while let Some(c) = iter.next() {
41 'outer: while let Some(c) = iter.next() {
37 let start = left.chars().count();
42 let start = left.chars().count();
38
43
39− if c != '#' {
44+ if c != "#" {
40− left.push(c);
45+ left.push_str(c);
41 continue;
46 continue;
42 }
47 }
43
48
44 let (is_primary, close_pair) = match iter.next() {
49 let (is_primary, close_pair) = match iter.next() {
45− Some('[') => (true, ']'),
50+ Some("[") => (true, "]"),
46− Some('(') => (false, ')'),
51+ Some("(") => (false, ")"),
47 Some(ch) => {
52 Some(ch) => {
48 left.push('#');
53 left.push('#');
49− left.push(ch);
54+ left.push_str(ch);
50 continue;
55 continue;
51 }
56 }
52 None => break,
57 None => break,
@@ -56,24 +61,45 @@ pub fn print(s: &str) -> (String, Selection) {
56 panic!("primary `#[` already appeared {:?} {:?}", left, s);
61 panic!("primary `#[` already appeared {:?} {:?}", left, s);
57 }
62 }
58
63
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+ };
60
70
61 while let Some(c) = iter.next() {
71 while let Some(c) = iter.next() {
72+ let next = iter.peek();
73+ let mut prev = last_grapheme(left.as_str());
74+
62− if !(c == close_pair && iter.peek() == Some(&'#')) {
75+ if !(c == close_pair && next == Some(&"#")) {
63− left.push(c);
76+ left.push_str(c);
64 continue;
77 continue;
65 }
78 }
66
79
67 if !head_at_beg {
80 if !head_at_beg {
68− let prev = left.pop().unwrap();
81+ match &prev {
69− if prev != '|' {
82+ Some(p) if p != "|" => {
70− left.push(prev);
71− left.push(c);
83+ left.push_str(c);
72− continue;
84+ continue;
85+ }
86+ Some(p) if p == "|" => {
87+ left.pop().unwrap(); // pop the |
88+ prev = last_grapheme(left.as_str());
89+ }
90+ _ => (),
73 }
91 }
74 }
92 }
75
93
76 iter.next(); // skip "#"
94 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+ }
77
103
78 if is_primary {
104 if is_primary {
79 primary_idx = Some(ranges.len());
105 primary_idx = Some(ranges.len());
@@ -118,11 +144,11 @@ pub fn print(s: &str) -> (String, Selection) {
118/// use smallvec::smallvec;
144/// use smallvec::smallvec;
119///
145///
120/// assert_eq!(
146/// 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)),
122/// "#[a|]#b#(|c)#".to_owned()
148/// "#[a|]#b#(|c)#".to_owned()
123/// );
149/// );
124/// ```
150/// ```
125−pub fn plain(s: &str, selection: Selection) -> String {
151+pub fn plain(s: &str, selection: &Selection) -> String {
126 let primary = selection.primary_index();
152 let primary = selection.primary_index();
127 let mut out = String::with_capacity(s.len() + 5 * selection.len());
153 let mut out = String::with_capacity(s.len() + 5 * selection.len());
128 out.push_str(s);
154 out.push_str(s);
@@ -147,6 +173,7 @@ pub fn plain(s: &str, selection: Selection) -> String {
147 out
173 out
148}
174}
149
175
176+#[allow(clippy::module_inception)]
150#[cfg(test)]
177#[cfg(test)]
151#[allow(clippy::module_inception)]
178#[allow(clippy::module_inception)]
152mod test {
179mod test {
helix-core/src/textobject.rs+3−3
@@ -437,7 +437,7 @@ mod test {
437 let text = Rope::from(s.as_str());
437 let text = Rope::from(s.as_str());
438 let selection = selection
438 let selection = selection
439 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Inside, 1));
439 .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);
441 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
441 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
442 }
442 }
443 }
443 }
@@ -460,7 +460,7 @@ mod test {
460 let text = Rope::from(s.as_str());
460 let text = Rope::from(s.as_str());
461 let selection = selection
461 let selection = selection
462 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Inside, 2));
462 .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);
464 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
464 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
465 }
465 }
466 }
466 }
@@ -491,7 +491,7 @@ mod test {
491 let text = Rope::from(s.as_str());
491 let text = Rope::from(s.as_str());
492 let selection = selection
492 let selection = selection
493 .transform(|r| textobject_paragraph(text.slice(..), r, TextObject::Around, 1));
493 .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);
495 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
495 assert_eq!(actual, expected, "\nbefore: `{:?}`", before);
496 }
496 }
497 }
497 }
helix-term/tests/test/commands.rs+22−22
@@ -201,12 +201,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
201 .as_str(),
201 .as_str(),
202 "|echo foo<ret>",
202 "|echo foo<ret>",
203 platform_line(indoc! {"\
203 platform_line(indoc! {"\
204− #[|foo
204+ #[|foo\n]#
205− ]#
205+
206− #(|foo
206+ #(|foo\n)#
207− )#
207+
208− #(|foo
208+ #(|foo\n)#
209− )#
209+
210 "})
210 "})
211 .as_str(),
211 .as_str(),
212 ))
212 ))
@@ -222,12 +222,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
222 .as_str(),
222 .as_str(),
223 "!echo foo<ret>",
223 "!echo foo<ret>",
224 platform_line(indoc! {"\
224 platform_line(indoc! {"\
225− #[|foo
225+ #[|foo\n]#
226− ]#lorem
226+ lorem
227− #(|foo
227+ #(|foo\n)#
228− )#ipsum
228+ ipsum
229− #(|foo
229+ #(|foo\n)#
230− )#dolor
230+ dolor
231 "})
231 "})
232 .as_str(),
232 .as_str(),
233 ))
233 ))
@@ -243,12 +243,12 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
243 .as_str(),
243 .as_str(),
244 "<A-!>echo foo<ret>",
244 "<A-!>echo foo<ret>",
245 platform_line(indoc! {"\
245 platform_line(indoc! {"\
246− lorem#[|foo
246+ lorem#[|foo\n]#
247− ]#
247+
248− ipsum#(|foo
248+ ipsum#(|foo\n)#
249− )#
249+
250− dolor#(|foo
250+ dolor#(|foo\n)#
251− )#
251+
252 "})
252 "})
253 .as_str(),
253 .as_str(),
254 ))
254 ))
@@ -300,8 +300,8 @@ async fn test_extend_line() -> anyhow::Result<()> {
300 platform_line(indoc! {"\
300 platform_line(indoc! {"\
301 #[lorem
301 #[lorem
302 ipsum
302 ipsum
303− dolor
303+ dolor\n|]#
304− |]#
304+
305 "})
305 "})
306 .as_str(),
306 .as_str(),
307 ))
307 ))
@@ -318,8 +318,8 @@ async fn test_extend_line() -> anyhow::Result<()> {
318 "2x",
318 "2x",
319 platform_line(indoc! {"\
319 platform_line(indoc! {"\
320 #[lorem
320 #[lorem
321− ipsum
321+ ipsum\n|]#
322− |]#
322+
323 "})
323 "})
324 .as_str(),
324 .as_str(),
325 ))
325 ))