1 //! The textwrap library provides functions for word wrapping and
2 //! indenting text.
3 //!
4 //! # Wrapping Text
5 //!
6 //! Wrapping text can be very useful in command-line programs where
7 //! you want to format dynamic output nicely so it looks good in a
8 //! terminal. A quick example:
9 //!
10 //! ```
11 //! # #[cfg(feature = "smawk")] {
12 //! let text = "textwrap: a small library for wrapping text.";
13 //! assert_eq!(textwrap::wrap(text, 18),
14 //!            vec!["textwrap: a",
15 //!                 "small library for",
16 //!                 "wrapping text."]);
17 //! # }
18 //! ```
19 //!
20 //! The [`wrap`] function returns the individual lines, use [`fill`]
21 //! is you want the lines joined with `'\n'` to form a `String`.
22 //!
23 //! If you enable the `hyphenation` Cargo feature, you can get
24 //! automatic hyphenation for a number of languages:
25 //!
26 //! ```
27 //! #[cfg(feature = "hyphenation")] {
28 //! use hyphenation::{Language, Load, Standard};
29 //! use textwrap::{wrap, Options, WordSplitter};
30 //!
31 //! let text = "textwrap: a small library for wrapping text.";
32 //! let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
33 //! let options = Options::new(18).word_splitter(WordSplitter::Hyphenation(dictionary));
34 //! assert_eq!(wrap(text, &options),
35 //!            vec!["textwrap: a small",
36 //!                 "library for wrap-",
37 //!                 "ping text."]);
38 //! }
39 //! ```
40 //!
41 //! See also the [`unfill`] and [`refill`] functions which allow you to
42 //! manipulate already wrapped text.
43 //!
44 //! ## Wrapping Strings at Compile Time
45 //!
46 //! If your strings are known at compile time, please take a look at
47 //! the procedural macros from the [textwrap-macros] crate.
48 //!
49 //! ## Displayed Width vs Byte Size
50 //!
51 //! To word wrap text, one must know the width of each word so one can
52 //! know when to break lines. This library will by default measure the
53 //! width of text using the _displayed width_, not the size in bytes.
54 //! The `unicode-width` Cargo feature controls this.
55 //!
56 //! This is important for non-ASCII text. ASCII characters such as `a`
57 //! and `!` are simple and take up one column each. This means that
58 //! the displayed width is equal to the string length in bytes.
59 //! However, non-ASCII characters and symbols take up more than one
60 //! byte when UTF-8 encoded: `é` is `0xc3 0xa9` (two bytes) and `⚙` is
61 //! `0xe2 0x9a 0x99` (three bytes) in UTF-8, respectively.
62 //!
63 //! This is why we take care to use the displayed width instead of the
64 //! byte count when computing line lengths. All functions in this
65 //! library handle Unicode characters like this when the
66 //! `unicode-width` Cargo feature is enabled (it is enabled by
67 //! default).
68 //!
69 //! # Indentation and Dedentation
70 //!
71 //! The textwrap library also offers functions for adding a prefix to
72 //! every line of a string and to remove leading whitespace. As an
73 //! example, the [`indent`] function allows you to turn lines of text
74 //! into a bullet list:
75 //!
76 //! ```
77 //! let before = "\
78 //! foo
79 //! bar
80 //! baz
81 //! ";
82 //! let after = "\
83 //! * foo
84 //! * bar
85 //! * baz
86 //! ";
87 //! assert_eq!(textwrap::indent(before, "* "), after);
88 //! ```
89 //!
90 //! Removing leading whitespace is done with [`dedent`]:
91 //!
92 //! ```
93 //! let before = "
94 //!     Some
95 //!       indented
96 //!         text
97 //! ";
98 //! let after = "
99 //! Some
100 //!   indented
101 //!     text
102 //! ";
103 //! assert_eq!(textwrap::dedent(before), after);
104 //! ```
105 //!
106 //! # Cargo Features
107 //!
108 //! The textwrap library can be slimmed down as needed via a number of
109 //! Cargo features. This means you only pay for the features you
110 //! actually use.
111 //!
112 //! The full dependency graph, where dashed lines indicate optional
113 //! dependencies, is shown below:
114 //!
115 //! <img src="https://raw.githubusercontent.com/mgeisler/textwrap/master/images/textwrap-0.16.0.svg">
116 //!
117 //! ## Default Features
118 //!
119 //! These features are enabled by default:
120 //!
121 //! * `unicode-linebreak`: enables finding words using the
122 //!   [unicode-linebreak] crate, which implements the line breaking
123 //!   algorithm described in [Unicode Standard Annex
124 //!   #14](https://www.unicode.org/reports/tr14/).
125 //!
126 //!   This feature can be disabled if you are happy to find words
127 //!   separated by ASCII space characters only. People wrapping text
128 //!   with emojis or East-Asian characters will want most likely want
129 //!   to enable this feature. See [`WordSeparator`] for details.
130 //!
131 //! * `unicode-width`: enables correct width computation of non-ASCII
132 //!   characters via the [unicode-width] crate. Without this feature,
133 //!   every [`char`] is 1 column wide, except for emojis which are 2
134 //!   columns wide. See the [`core::display_width`] function for
135 //!   details.
136 //!
137 //!   This feature can be disabled if you only need to wrap ASCII
138 //!   text, or if the functions in [`core`] are used directly with
139 //!   [`core::Fragment`]s for which the widths have been computed in
140 //!   other ways.
141 //!
142 //! * `smawk`: enables linear-time wrapping of the whole paragraph via
143 //!   the [smawk] crate. See the [`wrap_algorithms::wrap_optimal_fit`]
144 //!   function for details on the optimal-fit algorithm.
145 //!
146 //!   This feature can be disabled if you only ever intend to use
147 //!   [`wrap_algorithms::wrap_first_fit`].
148 //!
149 //! <!-- begin binary-sizes -->
150 //!
151 //! With Rust 1.64.0, the size impact of the above features on your
152 //! binary is as follows:
153 //!
154 //! | Configuration                            |  Binary Size |    Delta |
155 //! | :---                                     |         ---: |     ---: |
156 //! | quick-and-dirty implementation           |       289 KB |     — KB |
157 //! | textwrap without default features        |       305 KB |    16 KB |
158 //! | textwrap with smawk                      |       317 KB |    28 KB |
159 //! | textwrap with unicode-width              |       309 KB |    20 KB |
160 //! | textwrap with unicode-linebreak          |       342 KB |    53 KB |
161 //!
162 //! <!-- end binary-sizes -->
163 //!
164 //! The above sizes are the stripped sizes and the binary is compiled
165 //! in release mode with this profile:
166 //!
167 //! ```toml
168 //! [profile.release]
169 //! lto = true
170 //! codegen-units = 1
171 //! ```
172 //!
173 //! See the [binary-sizes demo] if you want to reproduce these
174 //! results.
175 //!
176 //! ## Optional Features
177 //!
178 //! These Cargo features enable new functionality:
179 //!
180 //! * `terminal_size`: enables automatic detection of the terminal
181 //!   width via the [terminal_size] crate. See the
182 //!   [`Options::with_termwidth`] constructor for details.
183 //!
184 //! * `hyphenation`: enables language-sensitive hyphenation via the
185 //!   [hyphenation] crate. See the [`word_splitters::WordSplitter`]
186 //!   trait for details.
187 //!
188 //! [unicode-linebreak]: https://docs.rs/unicode-linebreak/
189 //! [unicode-width]: https://docs.rs/unicode-width/
190 //! [smawk]: https://docs.rs/smawk/
191 //! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/master/examples/binary-sizes
192 //! [textwrap-macros]: https://docs.rs/textwrap-macros/
193 //! [terminal_size]: https://docs.rs/terminal_size/
194 //! [hyphenation]: https://docs.rs/hyphenation/
195 
196 #![doc(html_root_url = "https://docs.rs/textwrap/0.16.0")]
197 #![forbid(unsafe_code)] // See https://github.com/mgeisler/textwrap/issues/210
198 #![deny(missing_docs)]
199 #![deny(missing_debug_implementations)]
200 #![allow(clippy::redundant_field_names)]
201 
202 // Make `cargo test` execute the README doctests.
203 #[cfg(doctest)]
204 #[doc = include_str!("../README.md")]
205 mod readme_doctest {}
206 
207 use std::borrow::Cow;
208 
209 mod indentation;
210 pub use crate::indentation::{dedent, indent};
211 
212 mod word_separators;
213 pub use word_separators::WordSeparator;
214 
215 pub mod word_splitters;
216 pub use word_splitters::WordSplitter;
217 
218 pub mod wrap_algorithms;
219 pub use wrap_algorithms::WrapAlgorithm;
220 
221 mod line_ending;
222 pub use line_ending::LineEnding;
223 
224 pub mod core;
225 
226 // This module is only active when running fuzz tests. It provides
227 // access to private helpers.
228 #[cfg(fuzzing)]
229 pub mod fuzzing;
230 
231 /// Holds configuration options for wrapping and filling text.
232 #[non_exhaustive]
233 #[derive(Debug, Clone)]
234 pub struct Options<'a> {
235     /// The width in columns at which the text will be wrapped.
236     pub width: usize,
237     /// Line ending used for breaking lines.
238     pub line_ending: LineEnding,
239     /// Indentation used for the first line of output. See the
240     /// [`Options::initial_indent`] method.
241     pub initial_indent: &'a str,
242     /// Indentation used for subsequent lines of output. See the
243     /// [`Options::subsequent_indent`] method.
244     pub subsequent_indent: &'a str,
245     /// Allow long words to be broken if they cannot fit on a line.
246     /// When set to `false`, some lines may be longer than
247     /// `self.width`. See the [`Options::break_words`] method.
248     pub break_words: bool,
249     /// Wrapping algorithm to use, see the implementations of the
250     /// [`wrap_algorithms::WrapAlgorithm`] trait for details.
251     pub wrap_algorithm: WrapAlgorithm,
252     /// The line breaking algorithm to use, see
253     /// [`word_separators::WordSeparator`] trait for an overview and
254     /// possible implementations.
255     pub word_separator: WordSeparator,
256     /// The method for splitting words. This can be used to prohibit
257     /// splitting words on hyphens, or it can be used to implement
258     /// language-aware machine hyphenation.
259     pub word_splitter: WordSplitter,
260 }
261 
262 impl<'a> From<&'a Options<'a>> for Options<'a> {
from(options: &'a Options<'a>) -> Self263     fn from(options: &'a Options<'a>) -> Self {
264         Self {
265             width: options.width,
266             line_ending: options.line_ending,
267             initial_indent: options.initial_indent,
268             subsequent_indent: options.subsequent_indent,
269             break_words: options.break_words,
270             word_separator: options.word_separator,
271             wrap_algorithm: options.wrap_algorithm,
272             word_splitter: options.word_splitter.clone(),
273         }
274     }
275 }
276 
277 impl<'a> From<usize> for Options<'a> {
from(width: usize) -> Self278     fn from(width: usize) -> Self {
279         Options::new(width)
280     }
281 }
282 
283 impl<'a> Options<'a> {
284     /// Creates a new [`Options`] with the specified width.
285     ///
286     /// The other fields are given default values as follows:
287     ///
288     /// ```
289     /// # use textwrap::{LineEnding, Options, WordSplitter, WordSeparator, WrapAlgorithm};
290     /// # let width = 80;
291     /// let options = Options::new(width);
292     /// assert_eq!(options.line_ending, LineEnding::LF);
293     /// assert_eq!(options.initial_indent, "");
294     /// assert_eq!(options.subsequent_indent, "");
295     /// assert_eq!(options.break_words, true);
296     ///
297     /// #[cfg(feature = "unicode-linebreak")]
298     /// assert_eq!(options.word_separator, WordSeparator::UnicodeBreakProperties);
299     /// #[cfg(not(feature = "unicode-linebreak"))]
300     /// assert_eq!(options.word_separator, WordSeparator::AsciiSpace);
301     ///
302     /// #[cfg(feature = "smawk")]
303     /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::new_optimal_fit());
304     /// #[cfg(not(feature = "smawk"))]
305     /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::FirstFit);
306     ///
307     /// assert_eq!(options.word_splitter, WordSplitter::HyphenSplitter);
308     /// ```
309     ///
310     /// Note that the default word separator and wrap algorithms
311     /// changes based on the available Cargo features. The best
312     /// available algorithms are used by default.
new(width: usize) -> Self313     pub const fn new(width: usize) -> Self {
314         Options {
315             width,
316             line_ending: LineEnding::LF,
317             initial_indent: "",
318             subsequent_indent: "",
319             break_words: true,
320             word_separator: WordSeparator::new(),
321             wrap_algorithm: WrapAlgorithm::new(),
322             word_splitter: WordSplitter::HyphenSplitter,
323         }
324     }
325 
326     /// Creates a new [`Options`] with `width` set to the current
327     /// terminal width. If the terminal width cannot be determined
328     /// (typically because the standard input and output is not
329     /// connected to a terminal), a width of 80 characters will be
330     /// used. Other settings use the same defaults as
331     /// [`Options::new`].
332     ///
333     /// Equivalent to:
334     ///
335     /// ```no_run
336     /// use textwrap::{termwidth, Options};
337     ///
338     /// let options = Options::new(termwidth());
339     /// ```
340     ///
341     /// **Note:** Only available when the `terminal_size` feature is
342     /// enabled.
343     #[cfg(feature = "terminal_size")]
with_termwidth() -> Self344     pub fn with_termwidth() -> Self {
345         Self::new(termwidth())
346     }
347 
348     /// Change [`self.line_ending`]. This specifies which of the
349     /// supported line endings should be used to break the lines of the
350     /// input text.
351     ///
352     /// # Examples
353     ///
354     /// ```
355     /// use textwrap::{refill, LineEnding, Options};
356     ///
357     /// let options = Options::new(15).line_ending(LineEnding::CRLF);
358     /// assert_eq!(refill("This is a little example.", options),
359     ///            "This is a\r\nlittle example.");
360     /// ```
361     ///
362     /// [`self.line_ending`]: #structfield.line_ending
line_ending(self, line_ending: LineEnding) -> Self363     pub fn line_ending(self, line_ending: LineEnding) -> Self {
364         Options {
365             line_ending,
366             ..self
367         }
368     }
369 
370     /// Change [`self.initial_indent`]. The initial indentation is
371     /// used on the very first line of output.
372     ///
373     /// # Examples
374     ///
375     /// Classic paragraph indentation can be achieved by specifying an
376     /// initial indentation and wrapping each paragraph by itself:
377     ///
378     /// ```
379     /// use textwrap::{wrap, Options};
380     ///
381     /// let options = Options::new(16).initial_indent("    ");
382     /// assert_eq!(wrap("This is a little example.", options),
383     ///            vec!["    This is a",
384     ///                 "little example."]);
385     /// ```
386     ///
387     /// [`self.initial_indent`]: #structfield.initial_indent
initial_indent(self, indent: &'a str) -> Self388     pub fn initial_indent(self, indent: &'a str) -> Self {
389         Options {
390             initial_indent: indent,
391             ..self
392         }
393     }
394 
395     /// Change [`self.subsequent_indent`]. The subsequent indentation
396     /// is used on lines following the first line of output.
397     ///
398     /// # Examples
399     ///
400     /// Combining initial and subsequent indentation lets you format a
401     /// single paragraph as a bullet list:
402     ///
403     /// ```
404     /// use textwrap::{wrap, Options};
405     ///
406     /// let options = Options::new(12)
407     ///     .initial_indent("* ")
408     ///     .subsequent_indent("  ");
409     /// #[cfg(feature = "smawk")]
410     /// assert_eq!(wrap("This is a little example.", options),
411     ///            vec!["* This is",
412     ///                 "  a little",
413     ///                 "  example."]);
414     ///
415     /// // Without the `smawk` feature, the wrapping is a little different:
416     /// #[cfg(not(feature = "smawk"))]
417     /// assert_eq!(wrap("This is a little example.", options),
418     ///            vec!["* This is a",
419     ///                 "  little",
420     ///                 "  example."]);
421     /// ```
422     ///
423     /// [`self.subsequent_indent`]: #structfield.subsequent_indent
subsequent_indent(self, indent: &'a str) -> Self424     pub fn subsequent_indent(self, indent: &'a str) -> Self {
425         Options {
426             subsequent_indent: indent,
427             ..self
428         }
429     }
430 
431     /// Change [`self.break_words`]. This controls if words longer
432     /// than `self.width` can be broken, or if they will be left
433     /// sticking out into the right margin.
434     ///
435     /// See [`Options::word_splitter`] instead if you want to control
436     /// hyphenation.
437     ///
438     /// # Examples
439     ///
440     /// ```
441     /// use textwrap::{wrap, Options};
442     ///
443     /// let options = Options::new(4).break_words(true);
444     /// assert_eq!(wrap("This is a little example.", options),
445     ///            vec!["This",
446     ///                 "is a",
447     ///                 "litt",
448     ///                 "le",
449     ///                 "exam",
450     ///                 "ple."]);
451     /// ```
452     ///
453     /// [`self.break_words`]: #structfield.break_words
break_words(self, setting: bool) -> Self454     pub fn break_words(self, setting: bool) -> Self {
455         Options {
456             break_words: setting,
457             ..self
458         }
459     }
460 
461     /// Change [`self.word_separator`].
462     ///
463     /// See [`word_separators::WordSeparator`] for details on the choices.
464     ///
465     /// [`self.word_separator`]: #structfield.word_separator
word_separator(self, word_separator: WordSeparator) -> Options<'a>466     pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> {
467         Options {
468             width: self.width,
469             line_ending: self.line_ending,
470             initial_indent: self.initial_indent,
471             subsequent_indent: self.subsequent_indent,
472             break_words: self.break_words,
473             word_separator: word_separator,
474             wrap_algorithm: self.wrap_algorithm,
475             word_splitter: self.word_splitter,
476         }
477     }
478 
479     /// Change [`self.wrap_algorithm`].
480     ///
481     /// See the [`wrap_algorithms::WrapAlgorithm`] trait for details on
482     /// the choices.
483     ///
484     /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm
wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a>485     pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> {
486         Options {
487             width: self.width,
488             line_ending: self.line_ending,
489             initial_indent: self.initial_indent,
490             subsequent_indent: self.subsequent_indent,
491             break_words: self.break_words,
492             word_separator: self.word_separator,
493             wrap_algorithm: wrap_algorithm,
494             word_splitter: self.word_splitter,
495         }
496     }
497 
498     /// Change [`self.word_splitter`]. The
499     /// [`word_splitters::WordSplitter`] is used to fit part of a word
500     /// into the current line when wrapping text.
501     ///
502     /// See [`Options::break_words`] instead if you want to control the
503     /// handling of words longer than the line width.
504     ///
505     /// # Examples
506     ///
507     /// ```
508     /// use textwrap::{wrap, Options, WordSplitter};
509     ///
510     /// // The default is WordSplitter::HyphenSplitter.
511     /// let options = Options::new(5);
512     /// assert_eq!(wrap("foo-bar-baz", &options),
513     ///            vec!["foo-", "bar-", "baz"]);
514     ///
515     /// // The word is now so long that break_words kick in:
516     /// let options = Options::new(5)
517     ///     .word_splitter(WordSplitter::NoHyphenation);
518     /// assert_eq!(wrap("foo-bar-baz", &options),
519     ///            vec!["foo-b", "ar-ba", "z"]);
520     ///
521     /// // If you want to breaks at all, disable both:
522     /// let options = Options::new(5)
523     ///     .break_words(false)
524     ///     .word_splitter(WordSplitter::NoHyphenation);
525     /// assert_eq!(wrap("foo-bar-baz", &options),
526     ///            vec!["foo-bar-baz"]);
527     /// ```
528     ///
529     /// [`self.word_splitter`]: #structfield.word_splitter
word_splitter(self, word_splitter: WordSplitter) -> Options<'a>530     pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> {
531         Options {
532             width: self.width,
533             line_ending: self.line_ending,
534             initial_indent: self.initial_indent,
535             subsequent_indent: self.subsequent_indent,
536             break_words: self.break_words,
537             word_separator: self.word_separator,
538             wrap_algorithm: self.wrap_algorithm,
539             word_splitter,
540         }
541     }
542 }
543 
544 /// Return the current terminal width.
545 ///
546 /// If the terminal width cannot be determined (typically because the
547 /// standard output is not connected to a terminal), a default width
548 /// of 80 characters will be used.
549 ///
550 /// # Examples
551 ///
552 /// Create an [`Options`] for wrapping at the current terminal width
553 /// with a two column margin to the left and the right:
554 ///
555 /// ```no_run
556 /// use textwrap::{termwidth, Options};
557 ///
558 /// let width = termwidth() - 4; // Two columns on each side.
559 /// let options = Options::new(width)
560 ///     .initial_indent("  ")
561 ///     .subsequent_indent("  ");
562 /// ```
563 ///
564 /// **Note:** Only available when the `terminal_size` Cargo feature is
565 /// enabled.
566 #[cfg(feature = "terminal_size")]
termwidth() -> usize567 pub fn termwidth() -> usize {
568     terminal_size::terminal_size().map_or(80, |(terminal_size::Width(w), _)| w.into())
569 }
570 
571 /// Fill a line of text at a given width.
572 ///
573 /// The result is a [`String`], complete with newlines between each
574 /// line. Use the [`wrap`] function if you need access to the
575 /// individual lines.
576 ///
577 /// The easiest way to use this function is to pass an integer for
578 /// `width_or_options`:
579 ///
580 /// ```
581 /// use textwrap::fill;
582 ///
583 /// assert_eq!(
584 ///     fill("Memory safety without garbage collection.", 15),
585 ///     "Memory safety\nwithout garbage\ncollection."
586 /// );
587 /// ```
588 ///
589 /// If you need to customize the wrapping, you can pass an [`Options`]
590 /// instead of an `usize`:
591 ///
592 /// ```
593 /// use textwrap::{fill, Options};
594 ///
595 /// let options = Options::new(15)
596 ///     .initial_indent("- ")
597 ///     .subsequent_indent("  ");
598 /// assert_eq!(
599 ///     fill("Memory safety without garbage collection.", &options),
600 ///     "- Memory safety\n  without\n  garbage\n  collection."
601 /// );
602 /// ```
fill<'a, Opt>(text: &str, width_or_options: Opt) -> String where Opt: Into<Options<'a>>,603 pub fn fill<'a, Opt>(text: &str, width_or_options: Opt) -> String
604 where
605     Opt: Into<Options<'a>>,
606 {
607     let options = width_or_options.into();
608 
609     if text.len() < options.width && !text.contains('\n') && options.initial_indent.is_empty() {
610         String::from(text.trim_end_matches(' '))
611     } else {
612         fill_slow_path(text, options)
613     }
614 }
615 
616 /// Slow path for fill.
617 ///
618 /// This is taken when `text` is longer than `options.width`.
fill_slow_path(text: &str, options: Options<'_>) -> String619 fn fill_slow_path(text: &str, options: Options<'_>) -> String {
620     // This will avoid reallocation in simple cases (no
621     // indentation, no hyphenation).
622     let mut result = String::with_capacity(text.len());
623 
624     let line_ending_str = options.line_ending.as_str();
625     for (i, line) in wrap(text, options).iter().enumerate() {
626         if i > 0 {
627             result.push_str(line_ending_str);
628         }
629         result.push_str(line);
630     }
631 
632     result
633 }
634 
635 /// Unpack a paragraph of already-wrapped text.
636 ///
637 /// This function attempts to recover the original text from a single
638 /// paragraph of text produced by the [`fill`] function. This means
639 /// that it turns
640 ///
641 /// ```text
642 /// textwrap: a small
643 /// library for
644 /// wrapping text.
645 /// ```
646 ///
647 /// back into
648 ///
649 /// ```text
650 /// textwrap: a small library for wrapping text.
651 /// ```
652 ///
653 /// In addition, it will recognize a common prefix and a common line
654 /// ending among the lines.
655 ///
656 /// The prefix of the first line is returned in
657 /// [`Options::initial_indent`] and the prefix (if any) of the the
658 /// other lines is returned in [`Options::subsequent_indent`].
659 ///
660 /// Line ending is returned in [`Options::line_ending`]. If line ending
661 /// can not be confidently detected (mixed or no line endings in the
662 /// input), [`LineEnding::LF`] will be returned.
663 ///
664 /// In addition to `' '`, the prefixes can consist of characters used
665 /// for unordered lists (`'-'`, `'+'`, and `'*'`) and block quotes
666 /// (`'>'`) in Markdown as well as characters often used for inline
667 /// comments (`'#'` and `'/'`).
668 ///
669 /// The text must come from a single wrapped paragraph. This means
670 /// that there can be no empty lines (`"\n\n"` or `"\r\n\r\n"`) within
671 /// the text. It is unspecified what happens if `unfill` is called on
672 /// more than one paragraph of text.
673 ///
674 /// # Examples
675 ///
676 /// ```
677 /// use textwrap::{LineEnding, unfill};
678 ///
679 /// let (text, options) = unfill("\
680 /// * This is an
681 ///   example of
682 ///   a list item.
683 /// ");
684 ///
685 /// assert_eq!(text, "This is an example of a list item.\n");
686 /// assert_eq!(options.initial_indent, "* ");
687 /// assert_eq!(options.subsequent_indent, "  ");
688 /// assert_eq!(options.line_ending, LineEnding::LF);
689 /// ```
unfill(text: &str) -> (String, Options<'_>)690 pub fn unfill(text: &str) -> (String, Options<'_>) {
691     let prefix_chars: &[_] = &[' ', '-', '+', '*', '>', '#', '/'];
692 
693     let mut options = Options::new(0);
694     for (idx, line) in text.lines().enumerate() {
695         options.width = std::cmp::max(options.width, core::display_width(line));
696         let without_prefix = line.trim_start_matches(prefix_chars);
697         let prefix = &line[..line.len() - without_prefix.len()];
698 
699         if idx == 0 {
700             options.initial_indent = prefix;
701         } else if idx == 1 {
702             options.subsequent_indent = prefix;
703         } else if idx > 1 {
704             for ((idx, x), y) in prefix.char_indices().zip(options.subsequent_indent.chars()) {
705                 if x != y {
706                     options.subsequent_indent = &prefix[..idx];
707                     break;
708                 }
709             }
710             if prefix.len() < options.subsequent_indent.len() {
711                 options.subsequent_indent = prefix;
712             }
713         }
714     }
715 
716     let mut unfilled = String::with_capacity(text.len());
717     let mut detected_line_ending = None;
718 
719     for (idx, (line, ending)) in line_ending::NonEmptyLines(text).enumerate() {
720         if idx == 0 {
721             unfilled.push_str(&line[options.initial_indent.len()..]);
722         } else {
723             unfilled.push(' ');
724             unfilled.push_str(&line[options.subsequent_indent.len()..]);
725         }
726         match (detected_line_ending, ending) {
727             (None, Some(_)) => detected_line_ending = ending,
728             (Some(LineEnding::CRLF), Some(LineEnding::LF)) => detected_line_ending = ending,
729             _ => (),
730         }
731     }
732 
733     // Add back a line ending if `text` ends with the one we detect.
734     if let Some(line_ending) = detected_line_ending {
735         if text.ends_with(line_ending.as_str()) {
736             unfilled.push_str(line_ending.as_str());
737         }
738     }
739 
740     options.line_ending = detected_line_ending.unwrap_or(LineEnding::LF);
741     (unfilled, options)
742 }
743 
744 /// Refill a paragraph of wrapped text with a new width.
745 ///
746 /// This function will first use the [`unfill`] function to remove
747 /// newlines from the text. Afterwards the text is filled again using
748 /// the [`fill`] function.
749 ///
750 /// The `new_width_or_options` argument specify the new width and can
751 /// specify other options as well — except for
752 /// [`Options::initial_indent`] and [`Options::subsequent_indent`],
753 /// which are deduced from `filled_text`.
754 ///
755 /// # Examples
756 ///
757 /// ```
758 /// use textwrap::refill;
759 ///
760 /// // Some loosely wrapped text. The "> " prefix is recognized automatically.
761 /// let text = "\
762 /// > Memory
763 /// > safety without garbage
764 /// > collection.
765 /// ";
766 ///
767 /// assert_eq!(refill(text, 20), "\
768 /// > Memory safety
769 /// > without garbage
770 /// > collection.
771 /// ");
772 ///
773 /// assert_eq!(refill(text, 40), "\
774 /// > Memory safety without garbage
775 /// > collection.
776 /// ");
777 ///
778 /// assert_eq!(refill(text, 60), "\
779 /// > Memory safety without garbage collection.
780 /// ");
781 /// ```
782 ///
783 /// You can also reshape bullet points:
784 ///
785 /// ```
786 /// use textwrap::refill;
787 ///
788 /// let text = "\
789 /// - This is my
790 ///   list item.
791 /// ";
792 ///
793 /// assert_eq!(refill(text, 20), "\
794 /// - This is my list
795 ///   item.
796 /// ");
797 /// ```
refill<'a, Opt>(filled_text: &str, new_width_or_options: Opt) -> String where Opt: Into<Options<'a>>,798 pub fn refill<'a, Opt>(filled_text: &str, new_width_or_options: Opt) -> String
799 where
800     Opt: Into<Options<'a>>,
801 {
802     let mut new_options = new_width_or_options.into();
803     let (text, options) = unfill(filled_text);
804     // The original line ending is kept by `unfill`.
805     let stripped = text.strip_suffix(options.line_ending.as_str());
806     let new_line_ending = new_options.line_ending.as_str();
807 
808     new_options.initial_indent = options.initial_indent;
809     new_options.subsequent_indent = options.subsequent_indent;
810     let mut refilled = fill(stripped.unwrap_or(&text), new_options);
811 
812     // Add back right line ending if we stripped one off above.
813     if stripped.is_some() {
814         refilled.push_str(new_line_ending);
815     }
816     refilled
817 }
818 
819 /// Wrap a line of text at a given width.
820 ///
821 /// The result is a vector of lines, each line is of type [`Cow<'_,
822 /// str>`](Cow), which means that the line will borrow from the input
823 /// `&str` if possible. The lines do not have trailing whitespace,
824 /// including a final `'\n'`. Please use the [`fill`] function if you
825 /// need a [`String`] instead.
826 ///
827 /// The easiest way to use this function is to pass an integer for
828 /// `width_or_options`:
829 ///
830 /// ```
831 /// use textwrap::wrap;
832 ///
833 /// let lines = wrap("Memory safety without garbage collection.", 15);
834 /// assert_eq!(lines, &[
835 ///     "Memory safety",
836 ///     "without garbage",
837 ///     "collection.",
838 /// ]);
839 /// ```
840 ///
841 /// If you need to customize the wrapping, you can pass an [`Options`]
842 /// instead of an `usize`:
843 ///
844 /// ```
845 /// use textwrap::{wrap, Options};
846 ///
847 /// let options = Options::new(15)
848 ///     .initial_indent("- ")
849 ///     .subsequent_indent("  ");
850 /// let lines = wrap("Memory safety without garbage collection.", &options);
851 /// assert_eq!(lines, &[
852 ///     "- Memory safety",
853 ///     "  without",
854 ///     "  garbage",
855 ///     "  collection.",
856 /// ]);
857 /// ```
858 ///
859 /// # Optimal-Fit Wrapping
860 ///
861 /// By default, `wrap` will try to ensure an even right margin by
862 /// finding breaks which avoid short lines. We call this an
863 /// “optimal-fit algorithm” since the line breaks are computed by
864 /// considering all possible line breaks. The alternative is a
865 /// “first-fit algorithm” which simply accumulates words until they no
866 /// longer fit on the line.
867 ///
868 /// As an example, using the first-fit algorithm to wrap the famous
869 /// Hamlet quote “To be, or not to be: that is the question” in a
870 /// narrow column with room for only 10 characters looks like this:
871 ///
872 /// ```
873 /// # use textwrap::{WrapAlgorithm::FirstFit, Options, wrap};
874 /// #
875 /// # let lines = wrap("To be, or not to be: that is the question",
876 /// #                  Options::new(10).wrap_algorithm(FirstFit));
877 /// # assert_eq!(lines.join("\n") + "\n", "\
878 /// To be, or
879 /// not to be:
880 /// that is
881 /// the
882 /// question
883 /// # ");
884 /// ```
885 ///
886 /// Notice how the second to last line is quite narrow because
887 /// “question” was too large to fit? The greedy first-fit algorithm
888 /// doesn’t look ahead, so it has no other option than to put
889 /// “question” onto its own line.
890 ///
891 /// With the optimal-fit wrapping algorithm, the previous lines are
892 /// shortened slightly in order to make the word “is” go into the
893 /// second last line:
894 ///
895 /// ```
896 /// # #[cfg(feature = "smawk")] {
897 /// # use textwrap::{Options, WrapAlgorithm, wrap};
898 /// #
899 /// # let lines = wrap(
900 /// #     "To be, or not to be: that is the question",
901 /// #     Options::new(10).wrap_algorithm(WrapAlgorithm::new_optimal_fit())
902 /// # );
903 /// # assert_eq!(lines.join("\n") + "\n", "\
904 /// To be,
905 /// or not to
906 /// be: that
907 /// is the
908 /// question
909 /// # "); }
910 /// ```
911 ///
912 /// Please see [`WrapAlgorithm`] for details on the choices.
913 ///
914 /// # Examples
915 ///
916 /// The returned iterator yields lines of type `Cow<'_, str>`. If
917 /// possible, the wrapped lines will borrow from the input string. As
918 /// an example, a hanging indentation, the first line can borrow from
919 /// the input, but the subsequent lines become owned strings:
920 ///
921 /// ```
922 /// use std::borrow::Cow::{Borrowed, Owned};
923 /// use textwrap::{wrap, Options};
924 ///
925 /// let options = Options::new(15).subsequent_indent("....");
926 /// let lines = wrap("Wrapping text all day long.", &options);
927 /// let annotated = lines
928 ///     .iter()
929 ///     .map(|line| match line {
930 ///         Borrowed(text) => format!("[Borrowed] {}", text),
931 ///         Owned(text) => format!("[Owned]    {}", text),
932 ///     })
933 ///     .collect::<Vec<_>>();
934 /// assert_eq!(
935 ///     annotated,
936 ///     &[
937 ///         "[Borrowed] Wrapping text",
938 ///         "[Owned]    ....all day",
939 ///         "[Owned]    ....long.",
940 ///     ]
941 /// );
942 /// ```
943 ///
944 /// ## Leading and Trailing Whitespace
945 ///
946 /// As a rule, leading whitespace (indentation) is preserved and
947 /// trailing whitespace is discarded.
948 ///
949 /// In more details, when wrapping words into lines, words are found
950 /// by splitting the input text on space characters. One or more
951 /// spaces (shown here as “␣”) are attached to the end of each word:
952 ///
953 /// ```text
954 /// "Foo␣␣␣bar␣baz" -> ["Foo␣␣␣", "bar␣", "baz"]
955 /// ```
956 ///
957 /// These words are then put into lines. The interword whitespace is
958 /// preserved, unless the lines are wrapped so that the `"Foo␣␣␣"`
959 /// word falls at the end of a line:
960 ///
961 /// ```
962 /// use textwrap::wrap;
963 ///
964 /// assert_eq!(wrap("Foo   bar baz", 10), vec!["Foo   bar", "baz"]);
965 /// assert_eq!(wrap("Foo   bar baz", 8), vec!["Foo", "bar baz"]);
966 /// ```
967 ///
968 /// Notice how the trailing whitespace is removed in both case: in the
969 /// first example, `"bar␣"` becomes `"bar"` and in the second case
970 /// `"Foo␣␣␣"` becomes `"Foo"`.
971 ///
972 /// Leading whitespace is preserved when the following word fits on
973 /// the first line. To understand this, consider how words are found
974 /// in a text with leading spaces:
975 ///
976 /// ```text
977 /// "␣␣foo␣bar" -> ["␣␣", "foo␣", "bar"]
978 /// ```
979 ///
980 /// When put into lines, the indentation is preserved if `"foo"` fits
981 /// on the first line, otherwise you end up with an empty line:
982 ///
983 /// ```
984 /// use textwrap::wrap;
985 ///
986 /// assert_eq!(wrap("  foo bar", 8), vec!["  foo", "bar"]);
987 /// assert_eq!(wrap("  foo bar", 4), vec!["", "foo", "bar"]);
988 /// ```
wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec<Cow<'_, str>> where Opt: Into<Options<'a>>,989 pub fn wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec<Cow<'_, str>>
990 where
991     Opt: Into<Options<'a>>,
992 {
993     let options: Options = width_or_options.into();
994     let line_ending_str = options.line_ending.as_str();
995 
996     let mut lines = Vec::new();
997     for line in text.split(line_ending_str) {
998         wrap_single_line(line, &options, &mut lines);
999     }
1000 
1001     lines
1002 }
1003 
wrap_single_line<'a>(line: &'a str, options: &Options<'_>, lines: &mut Vec<Cow<'a, str>>)1004 fn wrap_single_line<'a>(line: &'a str, options: &Options<'_>, lines: &mut Vec<Cow<'a, str>>) {
1005     let indent = if lines.is_empty() {
1006         options.initial_indent
1007     } else {
1008         options.subsequent_indent
1009     };
1010     if line.len() < options.width && indent.is_empty() {
1011         lines.push(Cow::from(line.trim_end_matches(' ')));
1012     } else {
1013         wrap_single_line_slow_path(line, options, lines)
1014     }
1015 }
1016 
1017 /// Wrap a single line of text.
1018 ///
1019 /// This is taken when `line` is longer than `options.width`.
wrap_single_line_slow_path<'a>( line: &'a str, options: &Options<'_>, lines: &mut Vec<Cow<'a, str>>, )1020 fn wrap_single_line_slow_path<'a>(
1021     line: &'a str,
1022     options: &Options<'_>,
1023     lines: &mut Vec<Cow<'a, str>>,
1024 ) {
1025     let initial_width = options
1026         .width
1027         .saturating_sub(core::display_width(options.initial_indent));
1028     let subsequent_width = options
1029         .width
1030         .saturating_sub(core::display_width(options.subsequent_indent));
1031     let line_widths = [initial_width, subsequent_width];
1032 
1033     let words = options.word_separator.find_words(line);
1034     let split_words = word_splitters::split_words(words, &options.word_splitter);
1035     let broken_words = if options.break_words {
1036         let mut broken_words = core::break_words(split_words, line_widths[1]);
1037         if !options.initial_indent.is_empty() {
1038             // Without this, the first word will always go into the
1039             // first line. However, since we break words based on the
1040             // _second_ line width, it can be wrong to unconditionally
1041             // put the first word onto the first line. An empty
1042             // zero-width word fixed this.
1043             broken_words.insert(0, core::Word::from(""));
1044         }
1045         broken_words
1046     } else {
1047         split_words.collect::<Vec<_>>()
1048     };
1049 
1050     let wrapped_words = options.wrap_algorithm.wrap(&broken_words, &line_widths);
1051 
1052     let mut idx = 0;
1053     for words in wrapped_words {
1054         let last_word = match words.last() {
1055             None => {
1056                 lines.push(Cow::from(""));
1057                 continue;
1058             }
1059             Some(word) => word,
1060         };
1061 
1062         // We assume here that all words are contiguous in `line`.
1063         // That is, the sum of their lengths should add up to the
1064         // length of `line`.
1065         let len = words
1066             .iter()
1067             .map(|word| word.len() + word.whitespace.len())
1068             .sum::<usize>()
1069             - last_word.whitespace.len();
1070 
1071         // The result is owned if we have indentation, otherwise we
1072         // can simply borrow an empty string.
1073         let mut result = if lines.is_empty() && !options.initial_indent.is_empty() {
1074             Cow::Owned(options.initial_indent.to_owned())
1075         } else if !lines.is_empty() && !options.subsequent_indent.is_empty() {
1076             Cow::Owned(options.subsequent_indent.to_owned())
1077         } else {
1078             // We can use an empty string here since string
1079             // concatenation for `Cow` preserves a borrowed value when
1080             // either side is empty.
1081             Cow::from("")
1082         };
1083 
1084         result += &line[idx..idx + len];
1085 
1086         if !last_word.penalty.is_empty() {
1087             result.to_mut().push_str(last_word.penalty);
1088         }
1089 
1090         lines.push(result);
1091 
1092         // Advance by the length of `result`, plus the length of
1093         // `last_word.whitespace` -- even if we had a penalty, we need
1094         // to skip over the whitespace.
1095         idx += len + last_word.whitespace.len();
1096     }
1097 }
1098 
1099 /// Wrap text into columns with a given total width.
1100 ///
1101 /// The `left_gap`, `middle_gap` and `right_gap` arguments specify the
1102 /// strings to insert before, between, and after the columns. The
1103 /// total width of all columns and all gaps is specified using the
1104 /// `total_width_or_options` argument. This argument can simply be an
1105 /// integer if you want to use default settings when wrapping, or it
1106 /// can be a [`Options`] value if you want to customize the wrapping.
1107 ///
1108 /// If the columns are narrow, it is recommended to set
1109 /// [`Options::break_words`] to `true` to prevent words from
1110 /// protruding into the margins.
1111 ///
1112 /// The per-column width is computed like this:
1113 ///
1114 /// ```
1115 /// # let (left_gap, middle_gap, right_gap) = ("", "", "");
1116 /// # let columns = 2;
1117 /// # let options = textwrap::Options::new(80);
1118 /// let inner_width = options.width
1119 ///     - textwrap::core::display_width(left_gap)
1120 ///     - textwrap::core::display_width(right_gap)
1121 ///     - textwrap::core::display_width(middle_gap) * (columns - 1);
1122 /// let column_width = inner_width / columns;
1123 /// ```
1124 ///
1125 /// The `text` is wrapped using [`wrap`] and the given `options`
1126 /// argument, but the width is overwritten to the computed
1127 /// `column_width`.
1128 ///
1129 /// # Panics
1130 ///
1131 /// Panics if `columns` is zero.
1132 ///
1133 /// # Examples
1134 ///
1135 /// ```
1136 /// use textwrap::wrap_columns;
1137 ///
1138 /// let text = "\
1139 /// This is an example text, which is wrapped into three columns. \
1140 /// Notice how the final column can be shorter than the others.";
1141 ///
1142 /// #[cfg(feature = "smawk")]
1143 /// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"),
1144 ///            vec!["| This is       | into three    | column can be  |",
1145 ///                 "| an example    | columns.      | shorter than   |",
1146 ///                 "| text, which   | Notice how    | the others.    |",
1147 ///                 "| is wrapped    | the final     |                |"]);
1148 ///
1149 /// // Without the `smawk` feature, the middle column is a little more uneven:
1150 /// #[cfg(not(feature = "smawk"))]
1151 /// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"),
1152 ///            vec!["| This is an    | three         | column can be  |",
1153 ///                 "| example text, | columns.      | shorter than   |",
1154 ///                 "| which is      | Notice how    | the others.    |",
1155 ///                 "| wrapped into  | the final     |                |"]);
wrap_columns<'a, Opt>( text: &str, columns: usize, total_width_or_options: Opt, left_gap: &str, middle_gap: &str, right_gap: &str, ) -> Vec<String> where Opt: Into<Options<'a>>,1156 pub fn wrap_columns<'a, Opt>(
1157     text: &str,
1158     columns: usize,
1159     total_width_or_options: Opt,
1160     left_gap: &str,
1161     middle_gap: &str,
1162     right_gap: &str,
1163 ) -> Vec<String>
1164 where
1165     Opt: Into<Options<'a>>,
1166 {
1167     assert!(columns > 0);
1168 
1169     let mut options: Options = total_width_or_options.into();
1170 
1171     let inner_width = options
1172         .width
1173         .saturating_sub(core::display_width(left_gap))
1174         .saturating_sub(core::display_width(right_gap))
1175         .saturating_sub(core::display_width(middle_gap) * (columns - 1));
1176 
1177     let column_width = std::cmp::max(inner_width / columns, 1);
1178     options.width = column_width;
1179     let last_column_padding = " ".repeat(inner_width % column_width);
1180     let wrapped_lines = wrap(text, options);
1181     let lines_per_column =
1182         wrapped_lines.len() / columns + usize::from(wrapped_lines.len() % columns > 0);
1183     let mut lines = Vec::new();
1184     for line_no in 0..lines_per_column {
1185         let mut line = String::from(left_gap);
1186         for column_no in 0..columns {
1187             match wrapped_lines.get(line_no + column_no * lines_per_column) {
1188                 Some(column_line) => {
1189                     line.push_str(column_line);
1190                     line.push_str(&" ".repeat(column_width - core::display_width(column_line)));
1191                 }
1192                 None => {
1193                     line.push_str(&" ".repeat(column_width));
1194                 }
1195             }
1196             if column_no == columns - 1 {
1197                 line.push_str(&last_column_padding);
1198             } else {
1199                 line.push_str(middle_gap);
1200             }
1201         }
1202         line.push_str(right_gap);
1203         lines.push(line);
1204     }
1205 
1206     lines
1207 }
1208 
1209 /// Fill `text` in-place without reallocating the input string.
1210 ///
1211 /// This function works by modifying the input string: some `' '`
1212 /// characters will be replaced by `'\n'` characters. The rest of the
1213 /// text remains untouched.
1214 ///
1215 /// Since we can only replace existing whitespace in the input with
1216 /// `'\n'` (there is no space for `"\r\n"`), we cannot do hyphenation
1217 /// nor can we split words longer than the line width. We also need to
1218 /// use `AsciiSpace` as the word separator since we need `' '`
1219 /// characters between words in order to replace some of them with a
1220 /// `'\n'`. Indentation is also ruled out. In other words,
1221 /// `fill_inplace(width)` behaves as if you had called [`fill`] with
1222 /// these options:
1223 ///
1224 /// ```
1225 /// # use textwrap::{core, LineEnding, Options, WordSplitter, WordSeparator, WrapAlgorithm};
1226 /// # let width = 80;
1227 /// Options::new(width)
1228 ///     .break_words(false)
1229 ///     .line_ending(LineEnding::LF)
1230 ///     .word_separator(WordSeparator::AsciiSpace)
1231 ///     .wrap_algorithm(WrapAlgorithm::FirstFit)
1232 ///     .word_splitter(WordSplitter::NoHyphenation);
1233 /// ```
1234 ///
1235 /// The wrap algorithm is [`WrapAlgorithm::FirstFit`] since this
1236 /// is the fastest algorithm — and the main reason to use
1237 /// `fill_inplace` is to get the string broken into newlines as fast
1238 /// as possible.
1239 ///
1240 /// A last difference is that (unlike [`fill`]) `fill_inplace` can
1241 /// leave trailing whitespace on lines. This is because we wrap by
1242 /// inserting a `'\n'` at the final whitespace in the input string:
1243 ///
1244 /// ```
1245 /// let mut text = String::from("Hello   World!");
1246 /// textwrap::fill_inplace(&mut text, 10);
1247 /// assert_eq!(text, "Hello  \nWorld!");
1248 /// ```
1249 ///
1250 /// If we didn't do this, the word `World!` would end up being
1251 /// indented. You can avoid this if you make sure that your input text
1252 /// has no double spaces.
1253 ///
1254 /// # Performance
1255 ///
1256 /// In benchmarks, `fill_inplace` is about twice as fast as [`fill`].
1257 /// Please see the [`linear`
1258 /// benchmark](https://github.com/mgeisler/textwrap/blob/master/benchmarks/linear.rs)
1259 /// for details.
fill_inplace(text: &mut String, width: usize)1260 pub fn fill_inplace(text: &mut String, width: usize) {
1261     let mut indices = Vec::new();
1262 
1263     let mut offset = 0;
1264     for line in text.split('\n') {
1265         let words = WordSeparator::AsciiSpace
1266             .find_words(line)
1267             .collect::<Vec<_>>();
1268         let wrapped_words = wrap_algorithms::wrap_first_fit(&words, &[width as f64]);
1269 
1270         let mut line_offset = offset;
1271         for words in &wrapped_words[..wrapped_words.len() - 1] {
1272             let line_len = words
1273                 .iter()
1274                 .map(|word| word.len() + word.whitespace.len())
1275                 .sum::<usize>();
1276 
1277             line_offset += line_len;
1278             // We've advanced past all ' ' characters -- want to move
1279             // one ' ' backwards and insert our '\n' there.
1280             indices.push(line_offset - 1);
1281         }
1282 
1283         // Advance past entire line, plus the '\n' which was removed
1284         // by the split call above.
1285         offset += line.len() + 1;
1286     }
1287 
1288     let mut bytes = std::mem::take(text).into_bytes();
1289     for idx in indices {
1290         bytes[idx] = b'\n';
1291     }
1292     *text = String::from_utf8(bytes).unwrap();
1293 }
1294 
1295 #[cfg(test)]
1296 mod tests {
1297     use super::*;
1298 
1299     #[cfg(feature = "hyphenation")]
1300     use hyphenation::{Language, Load, Standard};
1301 
1302     #[test]
options_agree_with_usize()1303     fn options_agree_with_usize() {
1304         let opt_usize = Options::from(42_usize);
1305         let opt_options = Options::new(42);
1306 
1307         assert_eq!(opt_usize.width, opt_options.width);
1308         assert_eq!(opt_usize.initial_indent, opt_options.initial_indent);
1309         assert_eq!(opt_usize.subsequent_indent, opt_options.subsequent_indent);
1310         assert_eq!(opt_usize.break_words, opt_options.break_words);
1311         assert_eq!(
1312             opt_usize.word_splitter.split_points("hello-world"),
1313             opt_options.word_splitter.split_points("hello-world")
1314         );
1315     }
1316 
1317     #[test]
no_wrap()1318     fn no_wrap() {
1319         assert_eq!(wrap("foo", 10), vec!["foo"]);
1320     }
1321 
1322     #[test]
wrap_simple()1323     fn wrap_simple() {
1324         assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]);
1325     }
1326 
1327     #[test]
to_be_or_not()1328     fn to_be_or_not() {
1329         assert_eq!(
1330             wrap(
1331                 "To be, or not to be, that is the question.",
1332                 Options::new(10).wrap_algorithm(WrapAlgorithm::FirstFit)
1333             ),
1334             vec!["To be, or", "not to be,", "that is", "the", "question."]
1335         );
1336     }
1337 
1338     #[test]
multiple_words_on_first_line()1339     fn multiple_words_on_first_line() {
1340         assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]);
1341     }
1342 
1343     #[test]
long_word()1344     fn long_word() {
1345         assert_eq!(wrap("foo", 0), vec!["f", "o", "o"]);
1346     }
1347 
1348     #[test]
long_words()1349     fn long_words() {
1350         assert_eq!(wrap("foo bar", 0), vec!["f", "o", "o", "b", "a", "r"]);
1351     }
1352 
1353     #[test]
max_width()1354     fn max_width() {
1355         assert_eq!(wrap("foo bar", usize::MAX), vec!["foo bar"]);
1356 
1357         let text = "Hello there! This is some English text. \
1358                     It should not be wrapped given the extents below.";
1359         assert_eq!(wrap(text, usize::MAX), vec![text]);
1360     }
1361 
1362     #[test]
leading_whitespace()1363     fn leading_whitespace() {
1364         assert_eq!(wrap("  foo bar", 6), vec!["  foo", "bar"]);
1365     }
1366 
1367     #[test]
leading_whitespace_empty_first_line()1368     fn leading_whitespace_empty_first_line() {
1369         // If there is no space for the first word, the first line
1370         // will be empty. This is because the string is split into
1371         // words like [" ", "foobar ", "baz"], which puts "foobar " on
1372         // the second line. We never output trailing whitespace
1373         assert_eq!(wrap(" foobar baz", 6), vec!["", "foobar", "baz"]);
1374     }
1375 
1376     #[test]
trailing_whitespace()1377     fn trailing_whitespace() {
1378         // Whitespace is only significant inside a line. After a line
1379         // gets too long and is broken, the first word starts in
1380         // column zero and is not indented.
1381         assert_eq!(wrap("foo     bar     baz  ", 5), vec!["foo", "bar", "baz"]);
1382     }
1383 
1384     #[test]
issue_99()1385     fn issue_99() {
1386         // We did not reset the in_whitespace flag correctly and did
1387         // not handle single-character words after a line break.
1388         assert_eq!(
1389             wrap("aaabbbccc x yyyzzzwww", 9),
1390             vec!["aaabbbccc", "x", "yyyzzzwww"]
1391         );
1392     }
1393 
1394     #[test]
issue_129()1395     fn issue_129() {
1396         // The dash is an em-dash which takes up four bytes. We used
1397         // to panic since we tried to index into the character.
1398         let options = Options::new(1).word_separator(WordSeparator::AsciiSpace);
1399         assert_eq!(wrap("x – x", options), vec!["x", "–", "x"]);
1400     }
1401 
1402     #[test]
wide_character_handling()1403     fn wide_character_handling() {
1404         assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]);
1405         assert_eq!(
1406             wrap(
1407                 "Hello, World!",
1408                 Options::new(15).word_separator(WordSeparator::AsciiSpace)
1409             ),
1410             vec!["Hello,", "World!"]
1411         );
1412 
1413         // Wide characters are allowed to break if the
1414         // unicode-linebreak feature is enabled.
1415         #[cfg(feature = "unicode-linebreak")]
1416         assert_eq!(
1417             wrap(
1418                 "Hello, World!",
1419                 Options::new(15).word_separator(WordSeparator::UnicodeBreakProperties),
1420             ),
1421             vec!["Hello, W", "orld!"]
1422         );
1423     }
1424 
1425     #[test]
empty_line_is_indented()1426     fn empty_line_is_indented() {
1427         // Previously, indentation was not applied to empty lines.
1428         // However, this is somewhat inconsistent and undesirable if
1429         // the indentation is something like a border ("| ") which you
1430         // want to apply to all lines, empty or not.
1431         let options = Options::new(10).initial_indent("!!!");
1432         assert_eq!(fill("", &options), "!!!");
1433     }
1434 
1435     #[test]
indent_single_line()1436     fn indent_single_line() {
1437         let options = Options::new(10).initial_indent(">>>"); // No trailing space
1438         assert_eq!(fill("foo", &options), ">>>foo");
1439     }
1440 
1441     #[test]
indent_first_emoji()1442     fn indent_first_emoji() {
1443         let options = Options::new(10).initial_indent("����");
1444         assert_eq!(
1445             wrap("x x x x x x x x x x x x x", &options),
1446             vec!["����x x x", "x x x x x", "x x x x x"]
1447         );
1448     }
1449 
1450     #[test]
indent_multiple_lines()1451     fn indent_multiple_lines() {
1452         let options = Options::new(6).initial_indent("* ").subsequent_indent("  ");
1453         assert_eq!(
1454             wrap("foo bar baz", &options),
1455             vec!["* foo", "  bar", "  baz"]
1456         );
1457     }
1458 
1459     #[test]
only_initial_indent_multiple_lines()1460     fn only_initial_indent_multiple_lines() {
1461         let options = Options::new(10).initial_indent("  ");
1462         assert_eq!(wrap("foo\nbar\nbaz", &options), vec!["  foo", "bar", "baz"]);
1463     }
1464 
1465     #[test]
only_subsequent_indent_multiple_lines()1466     fn only_subsequent_indent_multiple_lines() {
1467         let options = Options::new(10).subsequent_indent("  ");
1468         assert_eq!(
1469             wrap("foo\nbar\nbaz", &options),
1470             vec!["foo", "  bar", "  baz"]
1471         );
1472     }
1473 
1474     #[test]
indent_break_words()1475     fn indent_break_words() {
1476         let options = Options::new(5).initial_indent("* ").subsequent_indent("  ");
1477         assert_eq!(wrap("foobarbaz", &options), vec!["* foo", "  bar", "  baz"]);
1478     }
1479 
1480     #[test]
initial_indent_break_words()1481     fn initial_indent_break_words() {
1482         // This is a corner-case showing how the long word is broken
1483         // according to the width of the subsequent lines. The first
1484         // fragment of the word no longer fits on the first line,
1485         // which ends up being pure indentation.
1486         let options = Options::new(5).initial_indent("-->");
1487         assert_eq!(wrap("foobarbaz", &options), vec!["-->", "fooba", "rbaz"]);
1488     }
1489 
1490     #[test]
hyphens()1491     fn hyphens() {
1492         assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]);
1493     }
1494 
1495     #[test]
trailing_hyphen()1496     fn trailing_hyphen() {
1497         let options = Options::new(5).break_words(false);
1498         assert_eq!(wrap("foobar-", &options), vec!["foobar-"]);
1499     }
1500 
1501     #[test]
multiple_hyphens()1502     fn multiple_hyphens() {
1503         assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]);
1504     }
1505 
1506     #[test]
hyphens_flag()1507     fn hyphens_flag() {
1508         let options = Options::new(5).break_words(false);
1509         assert_eq!(
1510             wrap("The --foo-bar flag.", &options),
1511             vec!["The", "--foo-", "bar", "flag."]
1512         );
1513     }
1514 
1515     #[test]
repeated_hyphens()1516     fn repeated_hyphens() {
1517         let options = Options::new(4).break_words(false);
1518         assert_eq!(wrap("foo--bar", &options), vec!["foo--bar"]);
1519     }
1520 
1521     #[test]
hyphens_alphanumeric()1522     fn hyphens_alphanumeric() {
1523         assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]);
1524     }
1525 
1526     #[test]
hyphens_non_alphanumeric()1527     fn hyphens_non_alphanumeric() {
1528         let options = Options::new(5).break_words(false);
1529         assert_eq!(wrap("foo(-)bar", &options), vec!["foo(-)bar"]);
1530     }
1531 
1532     #[test]
multiple_splits()1533     fn multiple_splits() {
1534         assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]);
1535     }
1536 
1537     #[test]
forced_split()1538     fn forced_split() {
1539         let options = Options::new(5).break_words(false);
1540         assert_eq!(wrap("foobar-baz", &options), vec!["foobar-", "baz"]);
1541     }
1542 
1543     #[test]
multiple_unbroken_words_issue_193()1544     fn multiple_unbroken_words_issue_193() {
1545         let options = Options::new(3).break_words(false);
1546         assert_eq!(
1547             wrap("small large tiny", &options),
1548             vec!["small", "large", "tiny"]
1549         );
1550         assert_eq!(
1551             wrap("small  large   tiny", &options),
1552             vec!["small", "large", "tiny"]
1553         );
1554     }
1555 
1556     #[test]
very_narrow_lines_issue_193()1557     fn very_narrow_lines_issue_193() {
1558         let options = Options::new(1).break_words(false);
1559         assert_eq!(wrap("fooo x y", &options), vec!["fooo", "x", "y"]);
1560         assert_eq!(wrap("fooo   x     y", &options), vec!["fooo", "x", "y"]);
1561     }
1562 
1563     #[test]
simple_hyphens()1564     fn simple_hyphens() {
1565         let options = Options::new(8).word_splitter(WordSplitter::HyphenSplitter);
1566         assert_eq!(wrap("foo bar-baz", &options), vec!["foo bar-", "baz"]);
1567     }
1568 
1569     #[test]
no_hyphenation()1570     fn no_hyphenation() {
1571         let options = Options::new(8).word_splitter(WordSplitter::NoHyphenation);
1572         assert_eq!(wrap("foo bar-baz", &options), vec!["foo", "bar-baz"]);
1573     }
1574 
1575     #[test]
1576     #[cfg(feature = "hyphenation")]
auto_hyphenation_double_hyphenation()1577     fn auto_hyphenation_double_hyphenation() {
1578         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1579         let options = Options::new(10);
1580         assert_eq!(
1581             wrap("Internationalization", &options),
1582             vec!["Internatio", "nalization"]
1583         );
1584 
1585         let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
1586         assert_eq!(
1587             wrap("Internationalization", &options),
1588             vec!["Interna-", "tionaliza-", "tion"]
1589         );
1590     }
1591 
1592     #[test]
1593     #[cfg(feature = "hyphenation")]
auto_hyphenation_issue_158()1594     fn auto_hyphenation_issue_158() {
1595         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1596         let options = Options::new(10);
1597         assert_eq!(
1598             wrap("participation is the key to success", &options),
1599             vec!["participat", "ion is", "the key to", "success"]
1600         );
1601 
1602         let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
1603         assert_eq!(
1604             wrap("participation is the key to success", &options),
1605             vec!["partici-", "pation is", "the key to", "success"]
1606         );
1607     }
1608 
1609     #[test]
1610     #[cfg(feature = "hyphenation")]
split_len_hyphenation()1611     fn split_len_hyphenation() {
1612         // Test that hyphenation takes the width of the whitespace
1613         // into account.
1614         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1615         let options = Options::new(15).word_splitter(WordSplitter::Hyphenation(dictionary));
1616         assert_eq!(
1617             wrap("garbage   collection", &options),
1618             vec!["garbage   col-", "lection"]
1619         );
1620     }
1621 
1622     #[test]
1623     #[cfg(feature = "hyphenation")]
borrowed_lines()1624     fn borrowed_lines() {
1625         // Lines that end with an extra hyphen are owned, the final
1626         // line is borrowed.
1627         use std::borrow::Cow::{Borrowed, Owned};
1628         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1629         let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
1630         let lines = wrap("Internationalization", &options);
1631         assert_eq!(lines, vec!["Interna-", "tionaliza-", "tion"]);
1632         if let Borrowed(s) = lines[0] {
1633             assert!(false, "should not have been borrowed: {:?}", s);
1634         }
1635         if let Borrowed(s) = lines[1] {
1636             assert!(false, "should not have been borrowed: {:?}", s);
1637         }
1638         if let Owned(ref s) = lines[2] {
1639             assert!(false, "should not have been owned: {:?}", s);
1640         }
1641     }
1642 
1643     #[test]
1644     #[cfg(feature = "hyphenation")]
auto_hyphenation_with_hyphen()1645     fn auto_hyphenation_with_hyphen() {
1646         let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
1647         let options = Options::new(8).break_words(false);
1648         assert_eq!(
1649             wrap("over-caffinated", &options),
1650             vec!["over-", "caffinated"]
1651         );
1652 
1653         let options = options.word_splitter(WordSplitter::Hyphenation(dictionary));
1654         assert_eq!(
1655             wrap("over-caffinated", &options),
1656             vec!["over-", "caffi-", "nated"]
1657         );
1658     }
1659 
1660     #[test]
break_words()1661     fn break_words() {
1662         assert_eq!(wrap("foobarbaz", 3), vec!["foo", "bar", "baz"]);
1663     }
1664 
1665     #[test]
break_words_wide_characters()1666     fn break_words_wide_characters() {
1667         // Even the poor man's version of `ch_width` counts these
1668         // characters as wide.
1669         let options = Options::new(5).word_separator(WordSeparator::AsciiSpace);
1670         assert_eq!(wrap("Hello", options), vec!["He", "ll", "o"]);
1671     }
1672 
1673     #[test]
break_words_zero_width()1674     fn break_words_zero_width() {
1675         assert_eq!(wrap("foobar", 0), vec!["f", "o", "o", "b", "a", "r"]);
1676     }
1677 
1678     #[test]
break_long_first_word()1679     fn break_long_first_word() {
1680         assert_eq!(wrap("testx y", 4), vec!["test", "x y"]);
1681     }
1682 
1683     #[test]
break_words_line_breaks()1684     fn break_words_line_breaks() {
1685         assert_eq!(fill("ab\ncdefghijkl", 5), "ab\ncdefg\nhijkl");
1686         assert_eq!(fill("abcdefgh\nijkl", 5), "abcde\nfgh\nijkl");
1687     }
1688 
1689     #[test]
break_words_empty_lines()1690     fn break_words_empty_lines() {
1691         assert_eq!(
1692             fill("foo\nbar", &Options::new(2).break_words(false)),
1693             "foo\nbar"
1694         );
1695     }
1696 
1697     #[test]
preserve_line_breaks()1698     fn preserve_line_breaks() {
1699         assert_eq!(fill("", 80), "");
1700         assert_eq!(fill("\n", 80), "\n");
1701         assert_eq!(fill("\n\n\n", 80), "\n\n\n");
1702         assert_eq!(fill("test\n", 80), "test\n");
1703         assert_eq!(fill("test\n\na\n\n", 80), "test\n\na\n\n");
1704         assert_eq!(
1705             fill(
1706                 "1 3 5 7\n1 3 5 7",
1707                 Options::new(7).wrap_algorithm(WrapAlgorithm::FirstFit)
1708             ),
1709             "1 3 5 7\n1 3 5 7"
1710         );
1711         assert_eq!(
1712             fill(
1713                 "1 3 5 7\n1 3 5 7",
1714                 Options::new(5).wrap_algorithm(WrapAlgorithm::FirstFit)
1715             ),
1716             "1 3 5\n7\n1 3 5\n7"
1717         );
1718     }
1719 
1720     #[test]
preserve_line_breaks_with_whitespace()1721     fn preserve_line_breaks_with_whitespace() {
1722         assert_eq!(fill("  ", 80), "");
1723         assert_eq!(fill("  \n  ", 80), "\n");
1724         assert_eq!(fill("  \n \n  \n ", 80), "\n\n\n");
1725     }
1726 
1727     #[test]
non_breaking_space()1728     fn non_breaking_space() {
1729         let options = Options::new(5).break_words(false);
1730         assert_eq!(fill("foo bar baz", &options), "foo bar baz");
1731     }
1732 
1733     #[test]
non_breaking_hyphen()1734     fn non_breaking_hyphen() {
1735         let options = Options::new(5).break_words(false);
1736         assert_eq!(fill("foo‑bar‑baz", &options), "foo‑bar‑baz");
1737     }
1738 
1739     #[test]
fill_simple()1740     fn fill_simple() {
1741         assert_eq!(fill("foo bar baz", 10), "foo bar\nbaz");
1742     }
1743 
1744     #[test]
fill_colored_text()1745     fn fill_colored_text() {
1746         // The words are much longer than 6 bytes, but they remain
1747         // intact after filling the text.
1748         let green_hello = "\u{1b}[0m\u{1b}[32mHello\u{1b}[0m";
1749         let blue_world = "\u{1b}[0m\u{1b}[34mWorld!\u{1b}[0m";
1750         assert_eq!(
1751             fill(&(String::from(green_hello) + " " + blue_world), 6),
1752             String::from(green_hello) + "\n" + blue_world
1753         );
1754     }
1755 
1756     #[test]
fill_unicode_boundary()1757     fn fill_unicode_boundary() {
1758         // https://github.com/mgeisler/textwrap/issues/390
1759         fill("\u{1b}!Ͽ", 10);
1760     }
1761 
1762     #[test]
fill_inplace_empty()1763     fn fill_inplace_empty() {
1764         let mut text = String::from("");
1765         fill_inplace(&mut text, 80);
1766         assert_eq!(text, "");
1767     }
1768 
1769     #[test]
fill_inplace_simple()1770     fn fill_inplace_simple() {
1771         let mut text = String::from("foo bar baz");
1772         fill_inplace(&mut text, 10);
1773         assert_eq!(text, "foo bar\nbaz");
1774     }
1775 
1776     #[test]
fill_inplace_multiple_lines()1777     fn fill_inplace_multiple_lines() {
1778         let mut text = String::from("Some text to wrap over multiple lines");
1779         fill_inplace(&mut text, 12);
1780         assert_eq!(text, "Some text to\nwrap over\nmultiple\nlines");
1781     }
1782 
1783     #[test]
fill_inplace_long_word()1784     fn fill_inplace_long_word() {
1785         let mut text = String::from("Internationalization is hard");
1786         fill_inplace(&mut text, 10);
1787         assert_eq!(text, "Internationalization\nis hard");
1788     }
1789 
1790     #[test]
fill_inplace_no_hyphen_splitting()1791     fn fill_inplace_no_hyphen_splitting() {
1792         let mut text = String::from("A well-chosen example");
1793         fill_inplace(&mut text, 10);
1794         assert_eq!(text, "A\nwell-chosen\nexample");
1795     }
1796 
1797     #[test]
fill_inplace_newlines()1798     fn fill_inplace_newlines() {
1799         let mut text = String::from("foo bar\n\nbaz\n\n\n");
1800         fill_inplace(&mut text, 10);
1801         assert_eq!(text, "foo bar\n\nbaz\n\n\n");
1802     }
1803 
1804     #[test]
fill_inplace_newlines_reset_line_width()1805     fn fill_inplace_newlines_reset_line_width() {
1806         let mut text = String::from("1 3 5\n1 3 5 7 9\n1 3 5 7 9 1 3");
1807         fill_inplace(&mut text, 10);
1808         assert_eq!(text, "1 3 5\n1 3 5 7 9\n1 3 5 7 9\n1 3");
1809     }
1810 
1811     #[test]
fill_inplace_leading_whitespace()1812     fn fill_inplace_leading_whitespace() {
1813         let mut text = String::from("  foo bar baz");
1814         fill_inplace(&mut text, 10);
1815         assert_eq!(text, "  foo bar\nbaz");
1816     }
1817 
1818     #[test]
fill_inplace_trailing_whitespace()1819     fn fill_inplace_trailing_whitespace() {
1820         let mut text = String::from("foo bar baz  ");
1821         fill_inplace(&mut text, 10);
1822         assert_eq!(text, "foo bar\nbaz  ");
1823     }
1824 
1825     #[test]
fill_inplace_interior_whitespace()1826     fn fill_inplace_interior_whitespace() {
1827         // To avoid an unwanted indentation of "baz", it is important
1828         // to replace the final ' ' with '\n'.
1829         let mut text = String::from("foo  bar    baz");
1830         fill_inplace(&mut text, 10);
1831         assert_eq!(text, "foo  bar   \nbaz");
1832     }
1833 
1834     #[test]
unfill_simple()1835     fn unfill_simple() {
1836         let (text, options) = unfill("foo\nbar");
1837         assert_eq!(text, "foo bar");
1838         assert_eq!(options.width, 3);
1839         assert_eq!(options.line_ending, LineEnding::LF);
1840     }
1841 
1842     #[test]
unfill_no_new_line()1843     fn unfill_no_new_line() {
1844         let (text, options) = unfill("foo bar");
1845         assert_eq!(text, "foo bar");
1846         assert_eq!(options.width, 7);
1847         assert_eq!(options.line_ending, LineEnding::LF);
1848     }
1849 
1850     #[test]
unfill_simple_crlf()1851     fn unfill_simple_crlf() {
1852         let (text, options) = unfill("foo\r\nbar");
1853         assert_eq!(text, "foo bar");
1854         assert_eq!(options.width, 3);
1855         assert_eq!(options.line_ending, LineEnding::CRLF);
1856     }
1857 
1858     #[test]
unfill_mixed_new_lines()1859     fn unfill_mixed_new_lines() {
1860         let (text, options) = unfill("foo\r\nbar\nbaz");
1861         assert_eq!(text, "foo bar baz");
1862         assert_eq!(options.width, 3);
1863         assert_eq!(options.line_ending, LineEnding::LF);
1864     }
1865 
1866     #[test]
unfill_trailing_newlines()1867     fn unfill_trailing_newlines() {
1868         let (text, options) = unfill("foo\nbar\n\n\n");
1869         assert_eq!(text, "foo bar\n");
1870         assert_eq!(options.width, 3);
1871     }
1872 
1873     #[test]
unfill_mixed_trailing_newlines()1874     fn unfill_mixed_trailing_newlines() {
1875         let (text, options) = unfill("foo\r\nbar\n\r\n\n");
1876         assert_eq!(text, "foo bar\n");
1877         assert_eq!(options.width, 3);
1878         assert_eq!(options.line_ending, LineEnding::LF);
1879     }
1880 
1881     #[test]
unfill_trailing_crlf()1882     fn unfill_trailing_crlf() {
1883         let (text, options) = unfill("foo bar\r\n");
1884         assert_eq!(text, "foo bar\r\n");
1885         assert_eq!(options.width, 7);
1886         assert_eq!(options.line_ending, LineEnding::CRLF);
1887     }
1888 
1889     #[test]
unfill_initial_indent()1890     fn unfill_initial_indent() {
1891         let (text, options) = unfill("  foo\nbar\nbaz");
1892         assert_eq!(text, "foo bar baz");
1893         assert_eq!(options.width, 5);
1894         assert_eq!(options.initial_indent, "  ");
1895     }
1896 
1897     #[test]
unfill_differing_indents()1898     fn unfill_differing_indents() {
1899         let (text, options) = unfill("  foo\n    bar\n  baz");
1900         assert_eq!(text, "foo   bar baz");
1901         assert_eq!(options.width, 7);
1902         assert_eq!(options.initial_indent, "  ");
1903         assert_eq!(options.subsequent_indent, "  ");
1904     }
1905 
1906     #[test]
unfill_list_item()1907     fn unfill_list_item() {
1908         let (text, options) = unfill("* foo\n  bar\n  baz");
1909         assert_eq!(text, "foo bar baz");
1910         assert_eq!(options.width, 5);
1911         assert_eq!(options.initial_indent, "* ");
1912         assert_eq!(options.subsequent_indent, "  ");
1913     }
1914 
1915     #[test]
unfill_multiple_char_prefix()1916     fn unfill_multiple_char_prefix() {
1917         let (text, options) = unfill("    // foo bar\n    // baz\n    // quux");
1918         assert_eq!(text, "foo bar baz quux");
1919         assert_eq!(options.width, 14);
1920         assert_eq!(options.initial_indent, "    // ");
1921         assert_eq!(options.subsequent_indent, "    // ");
1922     }
1923 
1924     #[test]
unfill_block_quote()1925     fn unfill_block_quote() {
1926         let (text, options) = unfill("> foo\n> bar\n> baz");
1927         assert_eq!(text, "foo bar baz");
1928         assert_eq!(options.width, 5);
1929         assert_eq!(options.initial_indent, "> ");
1930         assert_eq!(options.subsequent_indent, "> ");
1931     }
1932 
1933     #[test]
unfill_only_prefixes_issue_466()1934     fn unfill_only_prefixes_issue_466() {
1935         // Test that we don't crash if the first line has only prefix
1936         // chars *and* the second line is shorter than the first line.
1937         let (text, options) = unfill("######\nfoo");
1938         assert_eq!(text, " foo");
1939         assert_eq!(options.width, 6);
1940         assert_eq!(options.initial_indent, "######");
1941         assert_eq!(options.subsequent_indent, "");
1942     }
1943 
1944     #[test]
unfill_trailing_newlines_issue_466()1945     fn unfill_trailing_newlines_issue_466() {
1946         // Test that we don't crash on a '\r' following a string of
1947         // '\n'. The problem was that we removed both kinds of
1948         // characters in one code path, but not in the other.
1949         let (text, options) = unfill("foo\n##\n\n\r");
1950         // The \n\n changes subsequent_indent to "".
1951         assert_eq!(text, "foo ## \r");
1952         assert_eq!(options.width, 3);
1953         assert_eq!(options.initial_indent, "");
1954         assert_eq!(options.subsequent_indent, "");
1955     }
1956 
1957     #[test]
unfill_whitespace()1958     fn unfill_whitespace() {
1959         assert_eq!(unfill("foo   bar").0, "foo   bar");
1960     }
1961 
1962     #[test]
refill_convert_lf_to_crlf()1963     fn refill_convert_lf_to_crlf() {
1964         let options = Options::new(5).line_ending(LineEnding::CRLF);
1965         assert_eq!(refill("foo\nbar\n", options), "foo\r\nbar\r\n",);
1966     }
1967 
1968     #[test]
refill_convert_crlf_to_lf()1969     fn refill_convert_crlf_to_lf() {
1970         let options = Options::new(5).line_ending(LineEnding::LF);
1971         assert_eq!(refill("foo\r\nbar\r\n", options), "foo\nbar\n",);
1972     }
1973 
1974     #[test]
refill_convert_mixed_newlines()1975     fn refill_convert_mixed_newlines() {
1976         let options = Options::new(5).line_ending(LineEnding::CRLF);
1977         assert_eq!(refill("foo\r\nbar\n", options), "foo\r\nbar\r\n",);
1978     }
1979 
1980     #[test]
refill_defaults_to_lf()1981     fn refill_defaults_to_lf() {
1982         assert_eq!(refill("foo bar baz", 5), "foo\nbar\nbaz");
1983     }
1984 
1985     #[test]
wrap_columns_empty_text()1986     fn wrap_columns_empty_text() {
1987         assert_eq!(wrap_columns("", 1, 10, "| ", "", " |"), vec!["|        |"]);
1988     }
1989 
1990     #[test]
wrap_columns_single_column()1991     fn wrap_columns_single_column() {
1992         assert_eq!(
1993             wrap_columns("Foo", 3, 30, "| ", " | ", " |"),
1994             vec!["| Foo    |        |          |"]
1995         );
1996     }
1997 
1998     #[test]
wrap_columns_uneven_columns()1999     fn wrap_columns_uneven_columns() {
2000         // The gaps take up a total of 5 columns, so the columns are
2001         // (21 - 5)/4 = 4 columns wide:
2002         assert_eq!(
2003             wrap_columns("Foo Bar Baz Quux", 4, 21, "|", "|", "|"),
2004             vec!["|Foo |Bar |Baz |Quux|"]
2005         );
2006         // As the total width increases, the last column absorbs the
2007         // excess width:
2008         assert_eq!(
2009             wrap_columns("Foo Bar Baz Quux", 4, 24, "|", "|", "|"),
2010             vec!["|Foo |Bar |Baz |Quux   |"]
2011         );
2012         // Finally, when the width is 25, the columns can be resized
2013         // to a width of (25 - 5)/4 = 5 columns:
2014         assert_eq!(
2015             wrap_columns("Foo Bar Baz Quux", 4, 25, "|", "|", "|"),
2016             vec!["|Foo  |Bar  |Baz  |Quux |"]
2017         );
2018     }
2019 
2020     #[test]
2021     #[cfg(feature = "unicode-width")]
wrap_columns_with_emojis()2022     fn wrap_columns_with_emojis() {
2023         assert_eq!(
2024             wrap_columns(
2025                 "Words and a few emojis �� wrapped in ⓶ columns",
2026                 2,
2027                 30,
2028                 "✨ ",
2029                 " ⚽ ",
2030                 " ��"
2031             ),
2032             vec![
2033                 "✨ Words      ⚽ wrapped in ��",
2034                 "✨ and a few  ⚽ ⓶ columns  ��",
2035                 "✨ emojis ��  ⚽            ��"
2036             ]
2037         );
2038     }
2039 
2040     #[test]
wrap_columns_big_gaps()2041     fn wrap_columns_big_gaps() {
2042         // The column width shrinks to 1 because the gaps take up all
2043         // the space.
2044         assert_eq!(
2045             wrap_columns("xyz", 2, 10, "----> ", " !!! ", " <----"),
2046             vec![
2047                 "----> x !!! z <----", //
2048                 "----> y !!!   <----"
2049             ]
2050         );
2051     }
2052 
2053     #[test]
2054     #[should_panic]
wrap_columns_panic_with_zero_columns()2055     fn wrap_columns_panic_with_zero_columns() {
2056         wrap_columns("", 0, 10, "", "", "");
2057     }
2058 }
2059