1 //! Fork of `textwrap` crate 2 //! 3 //! Benefits of forking: 4 //! - Pull in only what we need rather than relying on the compiler to remove what we don't need 5 //! - `LineWrapper` is able to incrementally wrap which will help with `StyledStr 6 7 pub(crate) mod core; 8 #[cfg(feature = "wrap_help")] 9 pub(crate) mod word_separators; 10 #[cfg(feature = "wrap_help")] 11 pub(crate) mod wrap_algorithms; 12 13 #[cfg(feature = "wrap_help")] 14 pub(crate) fn wrap(content: &str, hard_width: usize) -> String { 15 let mut wrapper = wrap_algorithms::LineWrapper::new(hard_width); 16 let mut total = Vec::new(); 17 for line in content.split_inclusive('\n') { 18 wrapper.reset(); 19 let line = word_separators::find_words_ascii_space(line).collect::<Vec<_>>(); 20 total.extend(wrapper.wrap(line)); 21 } 22 total.join("") 23 } 24 25 #[cfg(not(feature = "wrap_help"))] 26 pub(crate) fn wrap(content: &str, _hard_width: usize) -> String { 27 content.to_owned() 28 } 29 30 #[cfg(test)] 31 #[cfg(feature = "wrap_help")] 32 mod test { 33 /// Compatibility shim to keep textwrap's tests wrapnull34 fn wrap(content: &str, hard_width: usize) -> Vec<String> { 35 super::wrap(content, hard_width) 36 .trim_end() 37 .split('\n') 38 .map(|s| s.to_owned()) 39 .collect::<Vec<_>>() 40 } 41 42 #[test] no_wrapnull43 fn no_wrap() { 44 assert_eq!(wrap("foo", 10), vec!["foo"]); 45 } 46 47 #[test] wrap_simplenull48 fn wrap_simple() { 49 assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]); 50 } 51 52 #[test] to_be_or_notnull53 fn to_be_or_not() { 54 assert_eq!( 55 wrap("To be, or not to be, that is the question.", 10), 56 vec!["To be, or", "not to be,", "that is", "the", "question."] 57 ); 58 } 59 60 #[test] multiple_words_on_first_linenull61 fn multiple_words_on_first_line() { 62 assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]); 63 } 64 65 #[test] long_wordnull66 fn long_word() { 67 assert_eq!(wrap("foo", 0), vec!["foo"]); 68 } 69 70 #[test] long_wordsnull71 fn long_words() { 72 assert_eq!(wrap("foo bar", 0), vec!["foo", "bar"]); 73 } 74 75 #[test] max_widthnull76 fn max_width() { 77 assert_eq!(wrap("foo bar", usize::MAX), vec!["foo bar"]); 78 79 let text = "Hello there! This is some English text. \ 80 It should not be wrapped given the extents below."; 81 assert_eq!(wrap(text, usize::MAX), vec![text]); 82 } 83 84 #[test] leading_whitespacenull85 fn leading_whitespace() { 86 assert_eq!(wrap(" foo bar", 6), vec![" foo", "bar"]); 87 } 88 89 #[test] leading_whitespace_empty_first_linenull90 fn leading_whitespace_empty_first_line() { 91 // If there is no space for the first word, the first line 92 // will be empty. This is because the string is split into 93 // words like [" ", "foobar ", "baz"], which puts "foobar " on 94 // the second line. We never output trailing whitespace 95 assert_eq!(wrap(" foobar baz", 6), vec!["", "foobar", "baz"]); 96 } 97 98 #[test] trailing_whitespacenull99 fn trailing_whitespace() { 100 // Whitespace is only significant inside a line. After a line 101 // gets too long and is broken, the first word starts in 102 // column zero and is not indented. 103 assert_eq!(wrap("foo bar baz ", 5), vec!["foo", "bar", "baz"]); 104 } 105 106 #[test] issue_99null107 fn issue_99() { 108 // We did not reset the in_whitespace flag correctly and did 109 // not handle single-character words after a line break. 110 assert_eq!( 111 wrap("aaabbbccc x yyyzzzwww", 9), 112 vec!["aaabbbccc", "x", "yyyzzzwww"] 113 ); 114 } 115 116 #[test] issue_129null117 fn issue_129() { 118 // The dash is an em-dash which takes up four bytes. We used 119 // to panic since we tried to index into the character. 120 assert_eq!(wrap("x – x", 1), vec!["x", "–", "x"]); 121 } 122 } 123