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