1 //! `textwrap` provides functions for word wrapping and filling text.
2 //!
3 //! Wrapping text can be very useful in commandline programs where you
4 //! want to format dynamic output nicely so it looks good in a
5 //! terminal. A quick example:
6 //!
7 //! ```no_run
8 //! fn main() {
9 //!     let text = "textwrap: a small library for wrapping text.";
10 //!     println!("{}", textwrap::fill(text, 18));
11 //! }
12 //! ```
13 //!
14 //! When you run this program, it will display the following output:
15 //!
16 //! ```text
17 //! textwrap: a small
18 //! library for
19 //! wrapping text.
20 //! ```
21 //!
22 //! If you enable the `hyphenation` feature, you can get automatic
23 //! hyphenation for a number of languages:
24 //!
25 //! ```no_run
26 //! # #[cfg(feature = "hyphenation")]
27 //! use hyphenation::{Language, Load, Standard};
28 //! use textwrap::Wrapper;
29 //!
30 //! # #[cfg(feature = "hyphenation")]
31 //! fn main() {
32 //!     let text = "textwrap: a small library for wrapping text.";
33 //!     let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
34 //!     let wrapper = Wrapper::with_splitter(18, dictionary);
35 //!     println!("{}", wrapper.fill(text));
36 //! }
37 //!
38 //! # #[cfg(not(feature = "hyphenation"))]
39 //! # fn main() { }
40 //! ```
41 //!
42 //! The program will now output:
43 //!
44 //! ```text
45 //! textwrap: a small
46 //! library for wrap-
47 //! ping text.
48 //! ```
49 //!
50 //! # Wrapping Strings at Compile Time
51 //!
52 //! If your strings are known at compile time, please take a look at
53 //! the procedural macros from the [`textwrap-macros` crate].
54 //!
55 //! # Displayed Width vs Byte Size
56 //!
57 //! To word wrap text, one must know the width of each word so one can
58 //! know when to break lines. This library measures the width of text
59 //! using the [displayed width][unicode-width], not the size in bytes.
60 //!
61 //! This is important for non-ASCII text. ASCII characters such as `a`
62 //! and `!` are simple and take up one column each. This means that
63 //! the displayed width is equal to the string length in bytes.
64 //! However, non-ASCII characters and symbols take up more than one
65 //! byte when UTF-8 encoded: `é` is `0xc3 0xa9` (two bytes) and `⚙` is
66 //! `0xe2 0x9a 0x99` (three bytes) in UTF-8, respectively.
67 //!
68 //! This is why we take care to use the displayed width instead of the
69 //! byte count when computing line lengths. All functions in this
70 //! library handle Unicode characters like this.
71 //!
72 //! # Cargo Features
73 //!
74 //! The library has two optional features:
75 //!
76 //! * `terminal_size`: enables automatic detection of the terminal
77 //!   width via the [terminal_size][] crate. See the
78 //!   [`Wrapper::with_termwidth`] constructor for details.
79 //!
80 //! * `hyphenation`: enables language-sentive hyphenation via the
81 //!   [hyphenation][] crate. See the [`WordSplitter`] trait for
82 //!   details.
83 //!
84 //! [`textwrap-macros` crate]: https://crates.io/crates/textwrap-macros
85 //! [unicode-width]: https://docs.rs/unicode-width/
86 //! [terminal_size]: https://crates.io/crates/terminal_size
87 //! [hyphenation]: https://crates.io/crates/hyphenation
88 //! [`Wrapper::with_termwidth`]: struct.Wrapper.html#method.with_termwidth
89 //! [`WordSplitter`]: trait.WordSplitter.html
90 
91 #![doc(html_root_url = "https://docs.rs/textwrap/0.12.1")]
92 #![deny(missing_docs)]
93 #![deny(missing_debug_implementations)]
94 #![allow(clippy::redundant_field_names)]
95 
96 use std::borrow::Cow;
97 use std::str::CharIndices;
98 
99 use unicode_width::UnicodeWidthChar;
100 use unicode_width::UnicodeWidthStr;
101 
102 /// A non-breaking space.
103 const NBSP: char = '\u{a0}';
104 
105 /// The CSI or "Control Sequence Introducer" introduces an ANSI escape
106 /// sequence. This is typically used for colored text and will be
107 /// ignored when computing the text width.
108 const CSI: (char, char) = ('\u{1b}', '[');
109 /// The final bytes of an ANSI escape sequence must be in this range.
110 const ANSI_FINAL_BYTE: std::ops::RangeInclusive<char> = '\x40'..='\x7e';
111 
112 mod indentation;
113 pub use crate::indentation::dedent;
114 pub use crate::indentation::indent;
115 
116 mod splitting;
117 pub use crate::splitting::{HyphenSplitter, NoHyphenation, WordSplitter};
118 
119 /// A Wrapper holds settings for wrapping and filling text. Use it
120 /// when the convenience [`wrap_iter`], [`wrap`] and [`fill`] functions
121 /// are not flexible enough.
122 ///
123 /// [`wrap_iter`]: fn.wrap_iter.html
124 /// [`wrap`]: fn.wrap.html
125 /// [`fill`]: fn.fill.html
126 ///
127 /// The algorithm used by the `WrapIter` iterator (returned from the
128 /// `wrap_iter` method)  works by doing successive partial scans over
129 /// words in the input string (where each single scan yields a single
130 /// line) so that the overall time and memory complexity is O(*n*) where
131 /// *n* is the length of the input string.
132 #[derive(Clone, Debug)]
133 pub struct Wrapper<'a, S: WordSplitter> {
134     /// The width in columns at which the text will be wrapped.
135     pub width: usize,
136     /// Indentation used for the first line of output.
137     pub initial_indent: &'a str,
138     /// Indentation used for subsequent lines of output.
139     pub subsequent_indent: &'a str,
140     /// Allow long words to be broken if they cannot fit on a line.
141     /// When set to `false`, some lines may be longer than
142     /// `self.width`.
143     pub break_words: bool,
144     /// The method for splitting words. If the `hyphenation` feature
145     /// is enabled, you can use a `hyphenation::Standard` dictionary
146     /// here to get language-aware hyphenation.
147     pub splitter: S,
148 }
149 
150 impl<'a> Wrapper<'a, HyphenSplitter> {
151     /// Create a new Wrapper for wrapping at the specified width. By
152     /// default, we allow words longer than `width` to be broken. A
153     /// [`HyphenSplitter`] will be used by default for splitting
154     /// words. See the [`WordSplitter`] trait for other options.
155     ///
156     /// [`HyphenSplitter`]: struct.HyphenSplitter.html
157     /// [`WordSplitter`]: trait.WordSplitter.html
new(width: usize) -> Wrapper<'a, HyphenSplitter>158     pub fn new(width: usize) -> Wrapper<'a, HyphenSplitter> {
159         Wrapper::with_splitter(width, HyphenSplitter)
160     }
161 
162     /// Create a new Wrapper for wrapping text at the current terminal
163     /// width. If the terminal width cannot be determined (typically
164     /// because the standard input and output is not connected to a
165     /// terminal), a width of 80 characters will be used. Other
166     /// settings use the same defaults as `Wrapper::new`.
167     ///
168     /// Equivalent to:
169     ///
170     /// ```no_run
171     /// # #![allow(unused_variables)]
172     /// use textwrap::{Wrapper, termwidth};
173     ///
174     /// let wrapper = Wrapper::new(termwidth());
175     /// ```
176     ///
177     /// **Note:** Only available when the `terminal_size` feature is
178     /// enabled.
179     #[cfg(feature = "terminal_size")]
with_termwidth() -> Wrapper<'a, HyphenSplitter>180     pub fn with_termwidth() -> Wrapper<'a, HyphenSplitter> {
181         Wrapper::new(termwidth())
182     }
183 }
184 
185 impl<'a, S: WordSplitter> Wrapper<'a, S> {
186     /// Use the given [`WordSplitter`] to create a new Wrapper for
187     /// wrapping at the specified width. By default, we allow words
188     /// longer than `width` to be broken.
189     ///
190     /// [`WordSplitter`]: trait.WordSplitter.html
with_splitter(width: usize, splitter: S) -> Wrapper<'a, S>191     pub fn with_splitter(width: usize, splitter: S) -> Wrapper<'a, S> {
192         Wrapper {
193             width: width,
194             initial_indent: "",
195             subsequent_indent: "",
196             break_words: true,
197             splitter: splitter,
198         }
199     }
200 
201     /// Change [`self.initial_indent`]. The initial indentation is
202     /// used on the very first line of output.
203     ///
204     /// # Examples
205     ///
206     /// Classic paragraph indentation can be achieved by specifying an
207     /// initial indentation and wrapping each paragraph by itself:
208     ///
209     /// ```no_run
210     /// # #![allow(unused_variables)]
211     /// use textwrap::Wrapper;
212     ///
213     /// let wrapper = Wrapper::new(15).initial_indent("    ");
214     /// ```
215     ///
216     /// [`self.initial_indent`]: #structfield.initial_indent
initial_indent(self, indent: &'a str) -> Wrapper<'a, S>217     pub fn initial_indent(self, indent: &'a str) -> Wrapper<'a, S> {
218         Wrapper {
219             initial_indent: indent,
220             ..self
221         }
222     }
223 
224     /// Change [`self.subsequent_indent`]. The subsequent indentation
225     /// is used on lines following the first line of output.
226     ///
227     /// # Examples
228     ///
229     /// Combining initial and subsequent indentation lets you format a
230     /// single paragraph as a bullet list:
231     ///
232     /// ```no_run
233     /// # #![allow(unused_variables)]
234     /// use textwrap::Wrapper;
235     ///
236     /// let wrapper = Wrapper::new(15)
237     ///     .initial_indent("* ")
238     ///     .subsequent_indent("  ");
239     /// ```
240     ///
241     /// [`self.subsequent_indent`]: #structfield.subsequent_indent
subsequent_indent(self, indent: &'a str) -> Wrapper<'a, S>242     pub fn subsequent_indent(self, indent: &'a str) -> Wrapper<'a, S> {
243         Wrapper {
244             subsequent_indent: indent,
245             ..self
246         }
247     }
248 
249     /// Change [`self.break_words`]. This controls if words longer
250     /// than `self.width` can be broken, or if they will be left
251     /// sticking out into the right margin.
252     ///
253     /// [`self.break_words`]: #structfield.break_words
break_words(self, setting: bool) -> Wrapper<'a, S>254     pub fn break_words(self, setting: bool) -> Wrapper<'a, S> {
255         Wrapper {
256             break_words: setting,
257             ..self
258         }
259     }
260 
261     /// Fill a line of text at `self.width` characters.
262     ///
263     /// The result is a string with newlines between each line. Use
264     /// the `wrap` method if you need access to the individual lines.
265     ///
266     /// # Complexities
267     ///
268     /// This method simply joins the lines produced by `wrap_iter`. As
269     /// such, it inherits the O(*n*) time and memory complexity where
270     /// *n* is the input string length.
271     ///
272     /// # Examples
273     ///
274     /// ```
275     /// use textwrap::Wrapper;
276     ///
277     /// let wrapper = Wrapper::new(15);
278     /// assert_eq!(wrapper.fill("Memory safety without garbage collection."),
279     ///            "Memory safety\nwithout garbage\ncollection.");
280     /// ```
fill(&self, s: &str) -> String281     pub fn fill(&self, s: &str) -> String {
282         // This will avoid reallocation in simple cases (no
283         // indentation, no hyphenation).
284         let mut result = String::with_capacity(s.len());
285 
286         for (i, line) in self.wrap_iter(s).enumerate() {
287             if i > 0 {
288                 result.push('\n');
289             }
290             result.push_str(&line);
291         }
292 
293         result
294     }
295 
296     /// Wrap a line of text at `self.width` characters.
297     ///
298     /// # Complexities
299     ///
300     /// This method simply collects the lines produced by `wrap_iter`.
301     /// As such, it inherits the O(*n*) overall time and memory
302     /// complexity where *n* is the input string length.
303     ///
304     /// # Examples
305     ///
306     /// ```
307     /// use textwrap::Wrapper;
308     ///
309     /// let wrap15 = Wrapper::new(15);
310     /// assert_eq!(wrap15.wrap("Concurrency without data races."),
311     ///            vec!["Concurrency",
312     ///                 "without data",
313     ///                 "races."]);
314     ///
315     /// let wrap20 = Wrapper::new(20);
316     /// assert_eq!(wrap20.wrap("Concurrency without data races."),
317     ///            vec!["Concurrency without",
318     ///                 "data races."]);
319     /// ```
320     ///
321     /// Notice that newlines in the input are preserved. This means
322     /// that they force a line break, regardless of how long the
323     /// current line is:
324     ///
325     /// ```
326     /// use textwrap::Wrapper;
327     ///
328     /// let wrapper = Wrapper::new(40);
329     /// assert_eq!(wrapper.wrap("First line.\nSecond line."),
330     ///            vec!["First line.", "Second line."]);
331     /// ```
332     ///
wrap(&self, s: &'a str) -> Vec<Cow<'a, str>>333     pub fn wrap(&self, s: &'a str) -> Vec<Cow<'a, str>> {
334         self.wrap_iter(s).collect::<Vec<_>>()
335     }
336 
337     /// Lazily wrap a line of text at `self.width` characters.
338     ///
339     /// The [`WordSplitter`] stored in [`self.splitter`] is used
340     /// whenever when a word is too large to fit on the current line.
341     /// By changing the field, different hyphenation strategies can be
342     /// implemented.
343     ///
344     /// # Complexities
345     ///
346     /// This method returns a [`WrapIter`] iterator which borrows this
347     /// `Wrapper`. The algorithm used has a linear complexity, so
348     /// getting the next line from the iterator will take O(*w*) time,
349     /// where *w* is the wrapping width. Fully processing the iterator
350     /// will take O(*n*) time for an input string of length *n*.
351     ///
352     /// When no indentation is used, each line returned is a slice of
353     /// the input string and the memory overhead is thus constant.
354     /// Otherwise new memory is allocated for each line returned.
355     ///
356     /// # Examples
357     ///
358     /// ```
359     /// use std::borrow::Cow::Borrowed;
360     /// use textwrap::Wrapper;
361     ///
362     /// let wrap20 = Wrapper::new(20);
363     /// let mut wrap20_iter = wrap20.wrap_iter("Zero-cost abstractions.");
364     /// assert_eq!(wrap20_iter.next(), Some(Borrowed("Zero-cost")));
365     /// assert_eq!(wrap20_iter.next(), Some(Borrowed("abstractions.")));
366     /// assert_eq!(wrap20_iter.next(), None);
367     ///
368     /// let wrap25 = Wrapper::new(25);
369     /// let mut wrap25_iter = wrap25.wrap_iter("Zero-cost abstractions.");
370     /// assert_eq!(wrap25_iter.next(), Some(Borrowed("Zero-cost abstractions.")));
371     /// assert_eq!(wrap25_iter.next(), None);
372     /// ```
373     ///
374     /// [`self.splitter`]: #structfield.splitter
375     /// [`WordSplitter`]: trait.WordSplitter.html
376     /// [`WrapIter`]: struct.WrapIter.html
wrap_iter<'w>(&'w self, s: &'a str) -> WrapIter<'w, 'a, S>377     pub fn wrap_iter<'w>(&'w self, s: &'a str) -> WrapIter<'w, 'a, S> {
378         WrapIter {
379             wrapper: self,
380             inner: WrapIterImpl::new(self, s),
381         }
382     }
383 
384     /// Lazily wrap a line of text at `self.width` characters.
385     ///
386     /// The [`WordSplitter`] stored in [`self.splitter`] is used
387     /// whenever when a word is too large to fit on the current line.
388     /// By changing the field, different hyphenation strategies can be
389     /// implemented.
390     ///
391     /// # Complexities
392     ///
393     /// This method consumes the `Wrapper` and returns a
394     /// [`IntoWrapIter`] iterator. Fully processing the iterator has
395     /// the same O(*n*) time complexity as [`wrap_iter`], where *n* is
396     /// the length of the input string.
397     ///
398     /// # Examples
399     ///
400     /// ```
401     /// use std::borrow::Cow::Borrowed;
402     /// use textwrap::Wrapper;
403     ///
404     /// let wrap20 = Wrapper::new(20);
405     /// let mut wrap20_iter = wrap20.into_wrap_iter("Zero-cost abstractions.");
406     /// assert_eq!(wrap20_iter.next(), Some(Borrowed("Zero-cost")));
407     /// assert_eq!(wrap20_iter.next(), Some(Borrowed("abstractions.")));
408     /// assert_eq!(wrap20_iter.next(), None);
409     /// ```
410     ///
411     /// [`self.splitter`]: #structfield.splitter
412     /// [`WordSplitter`]: trait.WordSplitter.html
413     /// [`IntoWrapIter`]: struct.IntoWrapIter.html
414     /// [`wrap_iter`]: #method.wrap_iter
into_wrap_iter(self, s: &'a str) -> IntoWrapIter<'a, S>415     pub fn into_wrap_iter(self, s: &'a str) -> IntoWrapIter<'a, S> {
416         let inner = WrapIterImpl::new(&self, s);
417 
418         IntoWrapIter {
419             wrapper: self,
420             inner: inner,
421         }
422     }
423 }
424 
425 /// An iterator over the lines of the input string which owns a
426 /// `Wrapper`. An instance of `IntoWrapIter` is typically obtained
427 /// through either [`wrap_iter`] or [`Wrapper::into_wrap_iter`].
428 ///
429 /// Each call of `.next()` method yields a line wrapped in `Some` if the
430 /// input hasn't been fully processed yet. Otherwise it returns `None`.
431 ///
432 /// [`wrap_iter`]: fn.wrap_iter.html
433 /// [`Wrapper::into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter
434 #[derive(Debug)]
435 pub struct IntoWrapIter<'a, S: WordSplitter> {
436     wrapper: Wrapper<'a, S>,
437     inner: WrapIterImpl<'a>,
438 }
439 
440 impl<'a, S: WordSplitter> Iterator for IntoWrapIter<'a, S> {
441     type Item = Cow<'a, str>;
442 
next(&mut self) -> Option<Cow<'a, str>>443     fn next(&mut self) -> Option<Cow<'a, str>> {
444         self.inner.next(&self.wrapper)
445     }
446 }
447 
448 /// An iterator over the lines of the input string which borrows a
449 /// `Wrapper`. An instance of `WrapIter` is typically obtained
450 /// through the [`Wrapper::wrap_iter`] method.
451 ///
452 /// Each call of `.next()` method yields a line wrapped in `Some` if the
453 /// input hasn't been fully processed yet. Otherwise it returns `None`.
454 ///
455 /// [`Wrapper::wrap_iter`]: struct.Wrapper.html#method.wrap_iter
456 #[derive(Debug)]
457 pub struct WrapIter<'w, 'a: 'w, S: WordSplitter> {
458     wrapper: &'w Wrapper<'a, S>,
459     inner: WrapIterImpl<'a>,
460 }
461 
462 impl<'w, 'a: 'w, S: WordSplitter> Iterator for WrapIter<'w, 'a, S> {
463     type Item = Cow<'a, str>;
464 
next(&mut self) -> Option<Cow<'a, str>>465     fn next(&mut self) -> Option<Cow<'a, str>> {
466         self.inner.next(self.wrapper)
467     }
468 }
469 
470 /// Like `char::is_whitespace`, but non-breaking spaces don't count.
471 #[inline]
is_whitespace(ch: char) -> bool472 fn is_whitespace(ch: char) -> bool {
473     ch.is_whitespace() && ch != NBSP
474 }
475 
476 /// Common implementation details for `WrapIter` and `IntoWrapIter`.
477 #[derive(Debug)]
478 struct WrapIterImpl<'a> {
479     // String to wrap.
480     source: &'a str,
481     // CharIndices iterator over self.source.
482     char_indices: CharIndices<'a>,
483     // Byte index where the current line starts.
484     start: usize,
485     // Byte index of the last place where the string can be split.
486     split: usize,
487     // Size in bytes of the character at self.source[self.split].
488     split_len: usize,
489     // Width of self.source[self.start..idx].
490     line_width: usize,
491     // Width of self.source[self.start..self.split].
492     line_width_at_split: usize,
493     // Tracking runs of whitespace characters.
494     in_whitespace: bool,
495     // Has iterator finished producing elements?
496     finished: bool,
497 }
498 
499 impl<'a> WrapIterImpl<'a> {
new<S: WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a>500     fn new<S: WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a> {
501         WrapIterImpl {
502             source: s,
503             char_indices: s.char_indices(),
504             start: 0,
505             split: 0,
506             split_len: 0,
507             line_width: wrapper.initial_indent.width(),
508             line_width_at_split: wrapper.initial_indent.width(),
509             in_whitespace: false,
510             finished: false,
511         }
512     }
513 
create_result_line<S: WordSplitter>(&self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str>514     fn create_result_line<S: WordSplitter>(&self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str> {
515         if self.start == 0 {
516             Cow::from(wrapper.initial_indent)
517         } else {
518             Cow::from(wrapper.subsequent_indent)
519         }
520     }
521 
next<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>>522     fn next<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>> {
523         if self.finished {
524             return None;
525         }
526 
527         while let Some((idx, ch)) = self.char_indices.next() {
528             if ch == CSI.0 && self.char_indices.next().map(|(_, ch)| ch) == Some(CSI.1) {
529                 // We have found the start of an ANSI escape code,
530                 // typically used for colored text. We ignore all
531                 // characters until we find a "final byte" in the
532                 // range 0x40–0x7E.
533                 while let Some((_, ch)) = self.char_indices.next() {
534                     if ANSI_FINAL_BYTE.contains(&ch) {
535                         break;
536                     }
537                 }
538                 // Done with the escape sequence, we continue with
539                 // next character in the outer loop.
540                 continue;
541             }
542 
543             let char_width = ch.width().unwrap_or(0);
544             let char_len = ch.len_utf8();
545             if ch == '\n' {
546                 self.split = idx;
547                 self.split_len = char_len;
548                 self.line_width_at_split = self.line_width;
549                 self.in_whitespace = false;
550 
551                 // If this is not the final line, return the current line. Otherwise,
552                 // we will return the line with its line break after exiting the loop
553                 if self.split + self.split_len < self.source.len() {
554                     let mut line = self.create_result_line(wrapper);
555                     line += &self.source[self.start..self.split];
556 
557                     self.start = self.split + self.split_len;
558                     self.line_width = wrapper.subsequent_indent.width();
559 
560                     return Some(line);
561                 }
562             } else if is_whitespace(ch) {
563                 // Extend the previous split or create a new one.
564                 if self.in_whitespace {
565                     self.split_len += char_len;
566                 } else {
567                     self.split = idx;
568                     self.split_len = char_len;
569                 }
570                 self.line_width_at_split = self.line_width + char_width;
571                 self.in_whitespace = true;
572             } else if self.line_width + char_width > wrapper.width {
573                 // There is no room for this character on the current
574                 // line. Try to split the final word.
575                 self.in_whitespace = false;
576                 let remaining_text = &self.source[self.split + self.split_len..];
577                 let final_word = match remaining_text.find(is_whitespace) {
578                     Some(i) => &remaining_text[..i],
579                     None => remaining_text,
580                 };
581 
582                 let mut hyphen = "";
583                 let splits = wrapper.splitter.split(final_word);
584                 for &(head, hyp, _) in splits.iter().rev() {
585                     if self.line_width_at_split + head.width() + hyp.width() <= wrapper.width {
586                         // We can fit head into the current line.
587                         // Advance the split point by the width of the
588                         // whitespace and the head length.
589                         self.split += self.split_len + head.len();
590                         // The new `split_len` is equal to the stretch
591                         // of whitespace following the split.
592                         self.split_len = remaining_text[head.len()..]
593                             .char_indices()
594                             .skip_while(|(_, ch)| is_whitespace(*ch))
595                             .next()
596                             .map_or(0, |(idx, _)| idx);
597                         self.line_width_at_split += head.width() + hyp.width();
598                         hyphen = hyp;
599                         break;
600                     }
601                 }
602 
603                 if self.start >= self.split {
604                     // The word is too big to fit on a single line.
605                     if wrapper.break_words {
606                         // Break work at current index.
607                         self.split = idx;
608                         self.split_len = 0;
609                         self.line_width_at_split = self.line_width;
610                     } else {
611                         // Add smallest split.
612                         self.split += self.split_len + splits[0].0.len();
613                         // The new `split_len` is equal to the stretch
614                         // of whitespace following the smallest split.
615                         self.split_len = remaining_text[splits[0].0.len()..]
616                             .char_indices()
617                             .skip_while(|(_, ch)| is_whitespace(*ch))
618                             .next()
619                             .map_or(0, |(idx, _)| idx);
620                         self.line_width_at_split = self.line_width;
621                     }
622                 }
623 
624                 if self.start < self.split {
625                     let mut line = self.create_result_line(wrapper);
626                     line += &self.source[self.start..self.split];
627                     line += hyphen;
628 
629                     self.start = self.split + self.split_len;
630                     self.line_width += wrapper.subsequent_indent.width();
631                     self.line_width -= self.line_width_at_split;
632                     self.line_width += char_width;
633                     self.line_width_at_split = wrapper.subsequent_indent.width();
634 
635                     return Some(line);
636                 }
637             } else {
638                 self.in_whitespace = false;
639             }
640             self.line_width += char_width;
641         }
642 
643         self.finished = true;
644 
645         // Add final line.
646         if self.start < self.source.len() {
647             let mut line = self.create_result_line(wrapper);
648             line += &self.source[self.start..];
649             return Some(line);
650         }
651 
652         None
653     }
654 }
655 
656 /// Return the current terminal width. If the terminal width cannot be
657 /// determined (typically because the standard output is not connected
658 /// to a terminal), a default width of 80 characters will be used.
659 ///
660 /// # Examples
661 ///
662 /// Create a `Wrapper` for the current terminal with a two column
663 /// margin:
664 ///
665 /// ```no_run
666 /// # #![allow(unused_variables)]
667 /// use textwrap::{Wrapper, NoHyphenation, termwidth};
668 ///
669 /// let width = termwidth() - 4; // Two columns on each side.
670 /// let wrapper = Wrapper::with_splitter(width, NoHyphenation)
671 ///     .initial_indent("  ")
672 ///     .subsequent_indent("  ");
673 /// ```
674 ///
675 /// **Note:** Only available when the `terminal_size` feature is
676 /// enabled.
677 #[cfg(feature = "terminal_size")]
termwidth() -> usize678 pub fn termwidth() -> usize {
679     terminal_size::terminal_size().map_or(80, |(terminal_size::Width(w), _)| w.into())
680 }
681 
682 /// Fill a line of text at `width` characters.
683 ///
684 /// The result is a string with newlines between each line. Use
685 /// [`wrap`] if you need access to the individual lines or
686 /// [`wrap_iter`] for its iterator counterpart.
687 ///
688 /// ```
689 /// use textwrap::fill;
690 ///
691 /// assert_eq!(fill("Memory safety without garbage collection.", 15),
692 ///            "Memory safety\nwithout garbage\ncollection.");
693 /// ```
694 ///
695 /// This function creates a Wrapper on the fly with default settings.
696 /// If you need to set a language corpus for automatic hyphenation, or
697 /// need to fill many strings, then it is suggested to create a Wrapper
698 /// and call its [`fill` method].
699 ///
700 /// [`wrap`]: fn.wrap.html
701 /// [`wrap_iter`]: fn.wrap_iter.html
702 /// [`fill` method]: struct.Wrapper.html#method.fill
fill(s: &str, width: usize) -> String703 pub fn fill(s: &str, width: usize) -> String {
704     Wrapper::new(width).fill(s)
705 }
706 
707 /// Wrap a line of text at `width` characters.
708 ///
709 /// This function creates a Wrapper on the fly with default settings.
710 /// If you need to set a language corpus for automatic hyphenation, or
711 /// need to wrap many strings, then it is suggested to create a Wrapper
712 /// and call its [`wrap` method].
713 ///
714 /// The result is a vector of strings. Use [`wrap_iter`] if you need an
715 /// iterator version.
716 ///
717 /// # Examples
718 ///
719 /// ```
720 /// use textwrap::wrap;
721 ///
722 /// assert_eq!(wrap("Concurrency without data races.", 15),
723 ///            vec!["Concurrency",
724 ///                 "without data",
725 ///                 "races."]);
726 ///
727 /// assert_eq!(wrap("Concurrency without data races.", 20),
728 ///            vec!["Concurrency without",
729 ///                 "data races."]);
730 /// ```
731 ///
732 /// [`wrap_iter`]: fn.wrap_iter.html
733 /// [`wrap` method]: struct.Wrapper.html#method.wrap
wrap(s: &str, width: usize) -> Vec<Cow<'_, str>>734 pub fn wrap(s: &str, width: usize) -> Vec<Cow<'_, str>> {
735     Wrapper::new(width).wrap(s)
736 }
737 
738 /// Lazily wrap a line of text at `width` characters.
739 ///
740 /// This function creates a Wrapper on the fly with default settings.
741 /// It then calls the [`into_wrap_iter`] method. Hence, the return
742 /// value is an [`IntoWrapIter`], not a [`WrapIter`] as the function
743 /// name would otherwise suggest.
744 ///
745 /// If you need to set a language corpus for automatic hyphenation, or
746 /// need to wrap many strings, then it is suggested to create a Wrapper
747 /// and call its [`wrap_iter`] or [`into_wrap_iter`] methods.
748 ///
749 /// # Examples
750 ///
751 /// ```
752 /// use std::borrow::Cow::Borrowed;
753 /// use textwrap::wrap_iter;
754 ///
755 /// let mut wrap20_iter = wrap_iter("Zero-cost abstractions.", 20);
756 /// assert_eq!(wrap20_iter.next(), Some(Borrowed("Zero-cost")));
757 /// assert_eq!(wrap20_iter.next(), Some(Borrowed("abstractions.")));
758 /// assert_eq!(wrap20_iter.next(), None);
759 ///
760 /// let mut wrap25_iter = wrap_iter("Zero-cost abstractions.", 25);
761 /// assert_eq!(wrap25_iter.next(), Some(Borrowed("Zero-cost abstractions.")));
762 /// assert_eq!(wrap25_iter.next(), None);
763 /// ```
764 ///
765 /// [`wrap_iter`]: struct.Wrapper.html#method.wrap_iter
766 /// [`into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter
767 /// [`IntoWrapIter`]: struct.IntoWrapIter.html
768 /// [`WrapIter`]: struct.WrapIter.html
wrap_iter(s: &str, width: usize) -> IntoWrapIter<'_, HyphenSplitter>769 pub fn wrap_iter(s: &str, width: usize) -> IntoWrapIter<'_, HyphenSplitter> {
770     Wrapper::new(width).into_wrap_iter(s)
771 }
772 
773 #[cfg(test)]
774 mod tests {
775     use super::*;
776     #[cfg(feature = "hyphenation")]
777     use hyphenation::{Language, Load, Standard};
778 
779     #[test]
no_wrap()780     fn no_wrap() {
781         assert_eq!(wrap("foo", 10), vec!["foo"]);
782     }
783 
784     #[test]
simple()785     fn simple() {
786         assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]);
787     }
788 
789     #[test]
multi_word_on_line()790     fn multi_word_on_line() {
791         assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]);
792     }
793 
794     #[test]
long_word()795     fn long_word() {
796         assert_eq!(wrap("foo", 0), vec!["f", "o", "o"]);
797     }
798 
799     #[test]
long_words()800     fn long_words() {
801         assert_eq!(wrap("foo bar", 0), vec!["f", "o", "o", "b", "a", "r"]);
802     }
803 
804     #[test]
max_width()805     fn max_width() {
806         assert_eq!(wrap("foo bar", usize::max_value()), vec!["foo bar"]);
807     }
808 
809     #[test]
leading_whitespace()810     fn leading_whitespace() {
811         assert_eq!(wrap("  foo bar", 6), vec!["  foo", "bar"]);
812     }
813 
814     #[test]
trailing_whitespace()815     fn trailing_whitespace() {
816         assert_eq!(wrap("foo bar  ", 6), vec!["foo", "bar  "]);
817     }
818 
819     #[test]
interior_whitespace()820     fn interior_whitespace() {
821         assert_eq!(wrap("foo:   bar baz", 10), vec!["foo:   bar", "baz"]);
822     }
823 
824     #[test]
extra_whitespace_start_of_line()825     fn extra_whitespace_start_of_line() {
826         // Whitespace is only significant inside a line. After a line
827         // gets too long and is broken, the first word starts in
828         // column zero and is not indented. The line before might end
829         // up with trailing whitespace.
830         assert_eq!(wrap("foo               bar", 5), vec!["foo", "bar"]);
831     }
832 
833     #[test]
issue_99()834     fn issue_99() {
835         // We did not reset the in_whitespace flag correctly and did
836         // not handle single-character words after a line break.
837         assert_eq!(
838             wrap("aaabbbccc x yyyzzzwww", 9),
839             vec!["aaabbbccc", "x", "yyyzzzwww"]
840         );
841     }
842 
843     #[test]
issue_129()844     fn issue_129() {
845         // The dash is an em-dash which takes up four bytes. We used
846         // to panic since we tried to index into the character.
847         assert_eq!(wrap("x – x", 1), vec!["x", "–", "x"]);
848     }
849 
850     #[test]
wide_character_handling()851     fn wide_character_handling() {
852         assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]);
853         assert_eq!(
854             wrap("Hello, World!", 15),
855             vec!["Hello,", "World!"]
856         );
857     }
858 
859     #[test]
empty_input_not_indented()860     fn empty_input_not_indented() {
861         let wrapper = Wrapper::new(10).initial_indent("!!!");
862         assert_eq!(wrapper.fill(""), "");
863     }
864 
865     #[test]
indent_single_line()866     fn indent_single_line() {
867         let wrapper = Wrapper::new(10).initial_indent(">>>"); // No trailing space
868         assert_eq!(wrapper.fill("foo"), ">>>foo");
869     }
870 
871     #[test]
indent_multiple_lines()872     fn indent_multiple_lines() {
873         let wrapper = Wrapper::new(6).initial_indent("* ").subsequent_indent("  ");
874         assert_eq!(wrapper.wrap("foo bar baz"), vec!["* foo", "  bar", "  baz"]);
875     }
876 
877     #[test]
indent_break_words()878     fn indent_break_words() {
879         let wrapper = Wrapper::new(5).initial_indent("* ").subsequent_indent("  ");
880         assert_eq!(wrapper.wrap("foobarbaz"), vec!["* foo", "  bar", "  baz"]);
881     }
882 
883     #[test]
hyphens()884     fn hyphens() {
885         assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]);
886     }
887 
888     #[test]
trailing_hyphen()889     fn trailing_hyphen() {
890         let wrapper = Wrapper::new(5).break_words(false);
891         assert_eq!(wrapper.wrap("foobar-"), vec!["foobar-"]);
892     }
893 
894     #[test]
multiple_hyphens()895     fn multiple_hyphens() {
896         assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]);
897     }
898 
899     #[test]
hyphens_flag()900     fn hyphens_flag() {
901         let wrapper = Wrapper::new(5).break_words(false);
902         assert_eq!(
903             wrapper.wrap("The --foo-bar flag."),
904             vec!["The", "--foo-", "bar", "flag."]
905         );
906     }
907 
908     #[test]
repeated_hyphens()909     fn repeated_hyphens() {
910         let wrapper = Wrapper::new(4).break_words(false);
911         assert_eq!(wrapper.wrap("foo--bar"), vec!["foo--bar"]);
912     }
913 
914     #[test]
hyphens_alphanumeric()915     fn hyphens_alphanumeric() {
916         assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]);
917     }
918 
919     #[test]
hyphens_non_alphanumeric()920     fn hyphens_non_alphanumeric() {
921         let wrapper = Wrapper::new(5).break_words(false);
922         assert_eq!(wrapper.wrap("foo(-)bar"), vec!["foo(-)bar"]);
923     }
924 
925     #[test]
multiple_splits()926     fn multiple_splits() {
927         assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]);
928     }
929 
930     #[test]
forced_split()931     fn forced_split() {
932         let wrapper = Wrapper::new(5).break_words(false);
933         assert_eq!(wrapper.wrap("foobar-baz"), vec!["foobar-", "baz"]);
934     }
935 
936     #[test]
multiple_unbroken_words_issue_193()937     fn multiple_unbroken_words_issue_193() {
938         let wrapper = Wrapper::new(3).break_words(false);
939         assert_eq!(
940             wrapper.wrap("small large tiny"),
941             vec!["small", "large", "tiny"]
942         );
943         assert_eq!(
944             wrapper.wrap("small  large   tiny"),
945             vec!["small", "large", "tiny"]
946         );
947     }
948 
949     #[test]
very_narrow_lines_issue_193()950     fn very_narrow_lines_issue_193() {
951         let wrapper = Wrapper::new(1).break_words(false);
952         assert_eq!(wrapper.wrap("fooo x y"), vec!["fooo", "x", "y"]);
953         assert_eq!(wrapper.wrap("fooo   x     y"), vec!["fooo", "x", "y"]);
954     }
955 
956     #[test]
no_hyphenation()957     fn no_hyphenation() {
958         let wrapper = Wrapper::with_splitter(8, NoHyphenation);
959         assert_eq!(wrapper.wrap("foo bar-baz"), vec!["foo", "bar-baz"]);
960     }
961 
962     #[test]
963     #[cfg(feature = "hyphenation")]
auto_hyphenation()964     fn auto_hyphenation() {
965         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
966         let wrapper = Wrapper::new(10);
967         assert_eq!(
968             wrapper.wrap("Internationalization"),
969             vec!["Internatio", "nalization"]
970         );
971 
972         let wrapper = Wrapper::with_splitter(10, dictionary);
973         assert_eq!(
974             wrapper.wrap("Internationalization"),
975             vec!["Interna-", "tionaliza-", "tion"]
976         );
977     }
978 
979     #[test]
980     #[cfg(feature = "hyphenation")]
auto_hyphenation_issue_158()981     fn auto_hyphenation_issue_158() {
982         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
983         let wrapper = Wrapper::new(10);
984         assert_eq!(
985             wrapper.wrap("participation is the key to success"),
986             vec!["participat", "ion is the", "key to", "success"]
987         );
988 
989         let wrapper = Wrapper::with_splitter(10, dictionary);
990         assert_eq!(
991             wrapper.wrap("participation is the key to success"),
992             vec!["participa-", "tion is the", "key to", "success"]
993         );
994     }
995 
996     #[test]
997     #[cfg(feature = "hyphenation")]
split_len_hyphenation()998     fn split_len_hyphenation() {
999         // Test that hyphenation takes the width of the wihtespace
1000         // into account.
1001         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1002         let wrapper = Wrapper::with_splitter(15, dictionary);
1003         assert_eq!(
1004             wrapper.wrap("garbage   collection"),
1005             vec!["garbage   col-", "lection"]
1006         );
1007     }
1008 
1009     #[test]
1010     #[cfg(feature = "hyphenation")]
borrowed_lines()1011     fn borrowed_lines() {
1012         // Lines that end with an extra hyphen are owned, the final
1013         // line is borrowed.
1014         use std::borrow::Cow::{Borrowed, Owned};
1015         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1016         let wrapper = Wrapper::with_splitter(10, dictionary);
1017         let lines = wrapper.wrap("Internationalization");
1018         if let Borrowed(s) = lines[0] {
1019             assert!(false, "should not have been borrowed: {:?}", s);
1020         }
1021         if let Borrowed(s) = lines[1] {
1022             assert!(false, "should not have been borrowed: {:?}", s);
1023         }
1024         if let Owned(ref s) = lines[2] {
1025             assert!(false, "should not have been owned: {:?}", s);
1026         }
1027     }
1028 
1029     #[test]
1030     #[cfg(feature = "hyphenation")]
auto_hyphenation_with_hyphen()1031     fn auto_hyphenation_with_hyphen() {
1032         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1033         let wrapper = Wrapper::new(8).break_words(false);
1034         assert_eq!(wrapper.wrap("over-caffinated"), vec!["over-", "caffinated"]);
1035 
1036         let wrapper = Wrapper::with_splitter(8, dictionary).break_words(false);
1037         assert_eq!(
1038             wrapper.wrap("over-caffinated"),
1039             vec!["over-", "caffi-", "nated"]
1040         );
1041     }
1042 
1043     #[test]
break_words()1044     fn break_words() {
1045         assert_eq!(wrap("foobarbaz", 3), vec!["foo", "bar", "baz"]);
1046     }
1047 
1048     #[test]
break_words_wide_characters()1049     fn break_words_wide_characters() {
1050         assert_eq!(wrap("Hello", 5), vec!["He", "ll", "o"]);
1051     }
1052 
1053     #[test]
break_words_zero_width()1054     fn break_words_zero_width() {
1055         assert_eq!(wrap("foobar", 0), vec!["f", "o", "o", "b", "a", "r"]);
1056     }
1057 
1058     #[test]
break_words_line_breaks()1059     fn break_words_line_breaks() {
1060         assert_eq!(fill("ab\ncdefghijkl", 5), "ab\ncdefg\nhijkl");
1061         assert_eq!(fill("abcdefgh\nijkl", 5), "abcde\nfgh\nijkl");
1062     }
1063 
1064     #[test]
preserve_line_breaks()1065     fn preserve_line_breaks() {
1066         assert_eq!(fill("test\n", 11), "test\n");
1067         assert_eq!(fill("test\n\na\n\n", 11), "test\n\na\n\n");
1068         assert_eq!(fill("1 3 5 7\n1 3 5 7", 7), "1 3 5 7\n1 3 5 7");
1069     }
1070 
1071     #[test]
wrap_preserve_line_breaks()1072     fn wrap_preserve_line_breaks() {
1073         assert_eq!(fill("1 3 5 7\n1 3 5 7", 5), "1 3 5\n7\n1 3 5\n7");
1074     }
1075 
1076     #[test]
non_breaking_space()1077     fn non_breaking_space() {
1078         let wrapper = Wrapper::new(5).break_words(false);
1079         assert_eq!(wrapper.fill("foo bar baz"), "foo bar baz");
1080     }
1081 
1082     #[test]
non_breaking_hyphen()1083     fn non_breaking_hyphen() {
1084         let wrapper = Wrapper::new(5).break_words(false);
1085         assert_eq!(wrapper.fill("foo‑bar‑baz"), "foo‑bar‑baz");
1086     }
1087 
1088     #[test]
fill_simple()1089     fn fill_simple() {
1090         assert_eq!(fill("foo bar baz", 10), "foo bar\nbaz");
1091     }
1092 
1093     #[test]
fill_colored_text()1094     fn fill_colored_text() {
1095         // The words are much longer than 6 bytes, but they remain
1096         // intact after filling the text.
1097         let green_hello = "\u{1b}[0m\u{1b}[32mHello\u{1b}[0m";
1098         let blue_world = "\u{1b}[0m\u{1b}[34mWorld!\u{1b}[0m";
1099         assert_eq!(
1100             fill(&(String::from(green_hello) + " " + &blue_world), 6),
1101             String::from(green_hello) + "\n" + &blue_world
1102         );
1103     }
1104 }
1105