Repository: mgeisler/textwrap (language: rust)

Issue:
Non breaking words
Hello,

When I run:

```rust
let text = "textwrap: a small library for wrapping text.";

println!(
    "{}",
    textwrap::Wrapper::new(1).break_words(false).fill(text)
);
```

I get the non-expected results:

```
textwrap:
 
a
small
 librar
y
 fo
r
 wrappin
g
 text
.
```

Yet I expect:

```
textwrap:
a
small
library
for
wrapping
text.
```

Same with 2, 3, 4 ...

Thank you!


Discussion / hints:
Hi @truchi! Oh wow, that looks odd... I'll take a look and try to fix it as soon as possible. Thanks for the report.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e96d6def..796b5861 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@ This file lists the most important changes made in each release of
 
 * Fixed [#176][issue-176]: Compile-time wrapping via the
   [`textwrap-macros` crate].
+* Fixed [#193][issue-193]: Non breaking words is broken.
 
 ## Version 0.12.0 — June 26th, 2020
 
@@ -172,3 +173,4 @@ First public release with support for wrapping strings on whitespace.
 [issue-151]: https://github.com/mgeisler/textwrap/issues/151
 [issue-158]: https://github.com/mgeisler/textwrap/issues/158
 [issue-177]: https://github.com/mgeisler/textwrap/issues/177
+[issue-193]: https://github.com/mgeisler/textwrap/issues/193
diff --git a/src/lib.rs b/src/lib.rs
index 06e3c36e..e3c59525 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -595,8 +595,7 @@ impl<'a> WrapIterImpl<'a> {
                 }
 
                 if self.start >= self.split {
-                    // The word is too big to fit on a single line, so we
-                    // need to split it at the current index.
+                    // The word is too big to fit on a single line.
                     if wrapper.break_words {
                         // Break work at current index.
                         self.split = idx;
@@ -604,8 +603,14 @@ impl<'a> WrapIterImpl<'a> {
                         self.line_width_at_split = self.line_width;
                     } else {
                         // Add smallest split.
-                        self.split = self.start + splits[0].0.len();
-                        self.split_len = 0;
+                        self.split += self.split_len + splits[0].0.len();
+                        // The new `split_len` is equal to the stretch
+                        // of whitespace following the smallest split.
+                        self.split_len = remaining_text[splits[0].0.len()..]
+                            .char_indices()
+                            .skip_while(|(_, ch)| is_whitespace(*ch))
+                            .next()
+                            .map_or(0, |(idx, _)| idx);
                         self.line_width_at_split = self.line_width;
                     }
                 }
@@ -922,6 +927,19 @@ mod tests {
         assert_eq!(wrapper.wrap("foobar-baz"), vec!["foobar-", "baz"]);
     }
 
+    #[test]
+    fn multiple_unbroken_words_issue_193() {
+        let wrapper = Wrapper::new(3).break_words(false);
+        assert_eq!(
+            wrapper.wrap("small large tiny"),
+            vec!["small", "large", "tiny"]
+        );
+        assert_eq!(
+            wrapper.wrap("small  large   tiny"),
+            vec!["small", "large", "tiny"]
+        );
+    }
+
     #[test]
     fn no_hyphenation() {
         let wrapper = Wrapper::with_splitter(8, NoHyphenation);


Test patch: