@@ -1,5 +1,6 @@
11//! Test helpers.
22use crate::{Range, Selection};
3+use ropey::Rope;
34use smallvec::SmallVec;
45use std::cmp::Reverse;
56use unicode_segmentation::UnicodeSegmentation;
@@ -148,10 +149,12 @@ pub fn print(s: &str) -> (String, Selection) {
148149/// "#[a|]#b#(|c)#".to_owned()
149150/// );
150151/// ```
151−pub fn plain(s: &str, selection: &Selection) -> String {
152+pub fn plain<R: Into<Rope>>(s: R, selection: &Selection) -> String {
153+ let s = s.into();
152154 let primary = selection.primary_index();
153− let mut out = String::with_capacity(s.len() + 5 * selection.len());
154− out.push_str(s);
155+ let mut out = String::with_capacity(s.len_bytes() + 5 * selection.len());
156+ out.push_str(&s.to_string());
157+
155158 let mut insertion: Vec<_> = selection
156159 .iter()
157160 .enumerate()
@@ -164,7 +167,9 @@ pub fn plain(s: &str, selection: &Selection) -> String {
164167 (false, false) => [(range.anchor, ")#"), (range.head, "#(|")],
165168 }
166169 })
170+ .map(|(char_idx, marker)| (s.char_to_byte(char_idx), marker))
167171 .collect();
172+
168173 // insert in reverse order
169174 insertion.sort_unstable_by_key(|k| Reverse(k.0));
170175 for (i, s) in insertion {
@@ -173,7 +178,6 @@ pub fn plain(s: &str, selection: &Selection) -> String {
173178 out
174179}
175180
176−#[allow(clippy::module_inception)]
177181#[cfg(test)]
178182#[allow(clippy::module_inception)]
179183mod test {
@@ -289,4 +293,94 @@ mod test {
289293 print("hello #[|👨👩👧👦]# goodbye")
290294 );
291295 }
296+
297+ #[test]
298+ fn plain_single() {
299+ assert_eq!("#[|h]#ello", plain("hello", &Selection::single(1, 0)));
300+ assert_eq!("#[h|]#ello", plain("hello", &Selection::single(0, 1)));
301+ assert_eq!("#[|hell]#o", plain("hello", &Selection::single(4, 0)));
302+ assert_eq!("#[hell|]#o", plain("hello", &Selection::single(0, 4)));
303+ assert_eq!("#[|hello]#", plain("hello", &Selection::single(5, 0)));
304+ assert_eq!("#[hello|]#", plain("hello", &Selection::single(0, 5)));
305+ }
306+
307+ #[test]
308+ fn plain_multi() {
309+ assert_eq!(
310+ plain(
311+ "hello",
312+ &Selection::new(
313+ SmallVec::from_slice(&[Range::new(1, 0), Range::new(5, 4)]),
314+ 0
315+ )
316+ ),
317+ String::from("#[|h]#ell#(|o)#")
318+ );
319+ assert_eq!(
320+ plain(
321+ "hello",
322+ &Selection::new(
323+ SmallVec::from_slice(&[Range::new(0, 1), Range::new(4, 5)]),
324+ 0
325+ )
326+ ),
327+ String::from("#[h|]#ell#(o|)#")
328+ );
329+ assert_eq!(
330+ plain(
331+ "hello",
332+ &Selection::new(
333+ SmallVec::from_slice(&[Range::new(2, 0), Range::new(5, 3)]),
334+ 0
335+ )
336+ ),
337+ String::from("#[|he]#l#(|lo)#")
338+ );
339+ assert_eq!(
340+ plain(
341+ "hello\r\nhello\r\nhello\r\n",
342+ &Selection::new(
343+ SmallVec::from_slice(&[
344+ Range::new(7, 5),
345+ Range::new(21, 19),
346+ Range::new(14, 12)
347+ ]),
348+ 0
349+ )
350+ ),
351+ String::from("hello#[|\r\n]#hello#(|\r\n)#hello#(|\r\n)#")
352+ );
353+ }
354+
355+ #[test]
356+ fn plain_multi_byte_code_point() {
357+ assert_eq!(
358+ plain("„“", &Selection::single(1, 0)),
359+ String::from("#[|„]#“")
360+ );
361+ assert_eq!(
362+ plain("„“", &Selection::single(2, 1)),
363+ String::from("„#[|“]#")
364+ );
365+ assert_eq!(
366+ plain("„“", &Selection::single(0, 1)),
367+ String::from("#[„|]#“")
368+ );
369+ assert_eq!(
370+ plain("„“", &Selection::single(1, 2)),
371+ String::from("„#[“|]#")
372+ );
373+ assert_eq!(
374+ plain("they said „hello“", &Selection::single(11, 10)),
375+ String::from("they said #[|„]#hello“")
376+ );
377+ }
378+
379+ #[test]
380+ fn plain_multi_code_point_grapheme() {
381+ assert_eq!(
382+ plain("hello 👨👩👧👦 goodbye", &Selection::single(13, 6)),
383+ String::from("hello #[|👨👩👧👦]# goodbye")
384+ );
385+ }
292386}