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