]> git.proxmox.com Git - rustc.git/blob - src/vendor/textwrap/src/lib.rs
New upstream version 1.22.1+dfsg1
[rustc.git] / src / vendor / textwrap / src / 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.8.0")]
45 #![deny(missing_docs)]
46
47 extern crate unicode_width;
48 extern crate term_size;
49 #[cfg(feature = "hyphenation")]
50 extern crate hyphenation;
51
52 use std::borrow::Cow;
53 use std::str::CharIndices;
54
55 use unicode_width::UnicodeWidthStr;
56 use unicode_width::UnicodeWidthChar;
57 #[cfg(feature = "hyphenation")]
58 use hyphenation::{Hyphenation, Corpus};
59
60 /// A non-breaking space.
61 const NBSP: char = '\u{a0}';
62
63 /// An interface for splitting words.
64 ///
65 /// When the [`wrap_iter`] method will try to fit text into a line, it
66 /// will eventually find a word that it too large the current text
67 /// width. It will then call the currently configured `WordSplitter` to
68 /// have it attempt to split the word into smaller parts. This trait
69 /// describes that functionality via the [`split`] method.
70 ///
71 /// If the `textwrap` crate has been compiled with the `hyphenation`
72 /// feature enabled, you will find an implementation of `WordSplitter`
73 /// by the `hyphenation::language::Corpus` struct. Use this struct for
74 /// language-aware hyphenation. See the [`hyphenation` documentation]
75 /// for details.
76 ///
77 /// [`wrap_iter`]: struct.Wrapper.html#method.wrap_iter
78 /// [`split`]: #tymethod.split
79 /// [`hyphenation` documentation]: https://docs.rs/hyphenation/
80 pub trait WordSplitter {
81 /// Return all possible splits of word. Each split is a triple
82 /// with a head, a hyphen, and a tail where `head + &hyphen +
83 /// &tail == word`. The hyphen can be empty if there is already a
84 /// hyphen in the head.
85 ///
86 /// The splits should go from smallest to longest and should
87 /// include no split at all. So the word "technology" could be
88 /// split into
89 ///
90 /// ```no_run
91 /// vec![("tech", "-", "nology"),
92 /// ("technol", "-", "ogy"),
93 /// ("technolo", "-", "gy"),
94 /// ("technology", "", "")];
95 /// ```
96 fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)>;
97 }
98
99 /// Use this as a [`Wrapper.splitter`] to avoid any kind of
100 /// hyphenation:
101 ///
102 /// ```
103 /// use textwrap::{Wrapper, NoHyphenation};
104 ///
105 /// let wrapper = Wrapper::with_splitter(8, NoHyphenation);
106 /// assert_eq!(wrapper.wrap("foo bar-baz"), vec!["foo", "bar-baz"]);
107 /// ```
108 ///
109 /// [`Wrapper.splitter`]: struct.Wrapper.html#structfield.splitter
110 #[derive(Clone)]
111 pub struct NoHyphenation;
112
113 /// `NoHyphenation` implements `WordSplitter` by not splitting the
114 /// word at all.
115 impl WordSplitter for NoHyphenation {
116 fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> {
117 vec![(word, "", "")]
118 }
119 }
120
121 /// Simple and default way to split words: splitting on existing
122 /// hyphens only.
123 ///
124 /// You probably don't need to use this type since it's already used
125 /// by default by `Wrapper::new`.
126 #[derive(Clone)]
127 pub struct HyphenSplitter;
128
129 /// `HyphenSplitter` is the default `WordSplitter` used by
130 /// `Wrapper::new`. It will split words on any existing hyphens in the
131 /// word.
132 ///
133 /// It will only use hyphens that are surrounded by alphanumeric
134 /// characters, which prevents a word like "--foo-bar" from being
135 /// split on the first or second hyphen.
136 impl WordSplitter for HyphenSplitter {
137 fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> {
138 let mut triples = Vec::new();
139 // Split on hyphens, smallest split first. We only use hyphens
140 // that are surrounded by alphanumeric characters. This is to
141 // avoid splitting on repeated hyphens, such as those found in
142 // --foo-bar.
143 let mut char_indices = word.char_indices();
144 // Early return if the word is empty.
145 let mut prev = match char_indices.next() {
146 None => return vec![(word, "", "")],
147 Some((_, ch)) => ch,
148 };
149
150 // Find current word, or return early if the word only has a
151 // single character.
152 let (mut idx, mut cur) = match char_indices.next() {
153 None => return vec![(word, "", "")],
154 Some((idx, cur)) => (idx, cur),
155 };
156
157 for (i, next) in char_indices {
158 if prev.is_alphanumeric() && cur == '-' && next.is_alphanumeric() {
159 let (head, tail) = word.split_at(idx + 1);
160 triples.push((head, "", tail));
161 }
162 prev = cur;
163 idx = i;
164 cur = next;
165 }
166
167 // Finally option is no split at all.
168 triples.push((word, "", ""));
169
170 triples
171 }
172 }
173
174 /// A hyphenation Corpus can be used to do language-specific
175 /// hyphenation using patterns from the hyphenation crate.
176 #[cfg(feature = "hyphenation")]
177 impl WordSplitter for Corpus {
178 fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> {
179 // Find splits based on language corpus.
180 let mut triples = Vec::new();
181 for n in word.opportunities(self) {
182 let (head, tail) = word.split_at(n);
183 let hyphen = if head.ends_with('-') { "" } else { "-" };
184 triples.push((head, hyphen, tail));
185 }
186 // Finally option is no split at all.
187 triples.push((word, "", ""));
188
189 triples
190 }
191 }
192
193 /// Backport of the `AddAssign` trait implementation from Rust 1.14.
194 fn cow_add_assign<'a>(lhs: &mut Cow<'a, str>, rhs: &'a str) {
195 if lhs.is_empty() {
196 *lhs = Cow::Borrowed(rhs)
197 } else if rhs.is_empty() {
198 return;
199 } else {
200 if let Cow::Borrowed(inner) = *lhs {
201 let mut s = String::with_capacity(lhs.len() + rhs.len());
202 s.push_str(inner);
203 *lhs = Cow::Owned(s);
204 }
205 lhs.to_mut().push_str(rhs);
206 }
207 }
208
209
210 /// A Wrapper holds settings for wrapping and filling text. Use it
211 /// when the convenience [`wrap_iter`], [`wrap`] and [`fill`] functions
212 /// are not flexible enough.
213 ///
214 /// [`wrap_iter`]: fn.wrap_iter.html
215 /// [`wrap`]: fn.wrap.html
216 /// [`fill`]: fn.fill.html
217 ///
218 /// The algorithm used by the `WrapIter` iterator (returned from the
219 /// `wrap_iter` method) works by doing successive partial scans over
220 /// words in the input string (where each single scan yields a single
221 /// line) so that the overall time and memory complexity is O(*n*) where
222 /// *n* is the length of the input string.
223 #[derive(Clone)]
224 pub struct Wrapper<'a, S: WordSplitter> {
225 /// The width in columns at which the text will be wrapped.
226 pub width: usize,
227 /// Indentation used for the first line of output.
228 pub initial_indent: &'a str,
229 /// Indentation used for subsequent lines of output.
230 pub subsequent_indent: &'a str,
231 /// Allow long words to be broken if they cannot fit on a line.
232 /// When set to `false`, some lines be being longer than
233 /// `self.width`.
234 pub break_words: bool,
235 /// The method for splitting words. If the `hyphenation` feature
236 /// is enabled, you can use a `hyphenation::language::Corpus` here
237 /// to get language-aware hyphenation.
238 pub splitter: S,
239 }
240
241 impl<'a> Wrapper<'a, HyphenSplitter> {
242 /// Create a new Wrapper for wrapping at the specified width. By
243 /// default, we allow words longer than `width` to be broken. A
244 /// [`HyphenSplitter`] will be used by default for splitting
245 /// words. See the [`WordSplitter`] trait for other options.
246 ///
247 /// [`HyphenSplitter`]: struct.HyphenSplitter.html
248 /// [`WordSplitter`]: trait.WordSplitter.html
249 pub fn new(width: usize) -> Wrapper<'a, HyphenSplitter> {
250 Wrapper::with_splitter(width, HyphenSplitter)
251 }
252
253 /// Create a new Wrapper for wrapping text at the current terminal
254 /// width. If the terminal width cannot be determined (typically
255 /// because the standard input and output is not connected to a
256 /// terminal), a width of 80 characters will be used. Other
257 /// settings use the same defaults as `Wrapper::new`.
258 ///
259 /// Equivalent to:
260 ///
261 /// ```no_run
262 /// use textwrap::{Wrapper, termwidth};
263 ///
264 /// let wrapper = Wrapper::new(termwidth());
265 /// ```
266 pub fn with_termwidth() -> Wrapper<'a, HyphenSplitter> {
267 Wrapper::new(termwidth())
268 }
269 }
270
271 impl<'w, 'a: 'w, S: WordSplitter> Wrapper<'a, S> {
272 /// Use the given [`WordSplitter`] to create a new Wrapper for
273 /// wrapping at the specified width. By default, we allow words
274 /// longer than `width` to be broken.
275 ///
276 /// [`WordSplitter`]: trait.WordSplitter.html
277 pub fn with_splitter(width: usize, splitter: S) -> Wrapper<'a, S> {
278 Wrapper {
279 width: width,
280 initial_indent: "",
281 subsequent_indent: "",
282 break_words: true,
283 splitter: splitter,
284 }
285 }
286
287 /// Change [`self.initial_indent`]. The initial indentation is
288 /// used on the very first line of output.
289 ///
290 /// # Examples
291 ///
292 /// Classic paragraph indentation can be achived by specifying an
293 /// initial indentation and wrapping each paragraph by itself:
294 ///
295 /// ```no_run
296 /// use textwrap::Wrapper;
297 ///
298 /// let wrapper = Wrapper::new(15).initial_indent(" ");
299 /// ```
300 ///
301 /// [`self.initial_indent`]: #structfield.initial_indent
302 pub fn initial_indent(self, indent: &'a str) -> Wrapper<'a, S> {
303 Wrapper { initial_indent: indent, ..self }
304 }
305
306 /// Change [`self.subsequent_indent`]. The subsequent indentation
307 /// is used on lines following the first line of output.
308 ///
309 /// # Examples
310 ///
311 /// Combining initial and subsequent indentation lets you format a
312 /// single paragraph as a bullet list:
313 ///
314 /// ```no_run
315 /// use textwrap::Wrapper;
316 ///
317 /// let wrapper = Wrapper::new(15)
318 /// .initial_indent("* ")
319 /// .subsequent_indent(" ");
320 /// ```
321 ///
322 /// [`self.subsequent_indent`]: #structfield.subsequent_indent
323 pub fn subsequent_indent(self, indent: &'a str) -> Wrapper<'a, S> {
324 Wrapper { subsequent_indent: indent, ..self }
325 }
326
327 /// Change [`self.break_words`]. This controls if words longer
328 /// than `self.width` can be broken, or if they will be left
329 /// sticking out into the right margin.
330 ///
331 /// [`self.break_words`]: #structfield.break_words
332 pub fn break_words(self, setting: bool) -> Wrapper<'a, S> {
333 Wrapper { break_words: setting, ..self }
334 }
335
336 /// Fill a line of text at `self.width` characters. Strings are
337 /// wrapped based on their displayed width, not their size in
338 /// bytes.
339 ///
340 /// The result is a string with newlines between each line. Use
341 /// the `wrap` method if you need access to the individual lines.
342 ///
343 /// # Complexities
344 ///
345 /// This method simply joins the lines produced by `wrap_iter`. As
346 /// such, it inherits the O(*n*) time and memory complexity where
347 /// *n* is the input string length.
348 ///
349 /// # Examples
350 ///
351 /// ```
352 /// use textwrap::Wrapper;
353 ///
354 /// let wrapper = Wrapper::new(15);
355 /// assert_eq!(wrapper.fill("Memory safety without garbage collection."),
356 /// "Memory safety\nwithout garbage\ncollection.");
357 /// ```
358 pub fn fill(&self, s: &str) -> String {
359 let mut result = String::new();
360
361 for (i, line) in self.wrap_iter(s).enumerate() {
362 if i > 0 {
363 result.push_str("\n");
364 }
365
366 result.push_str(&line);
367 }
368
369 result
370 }
371
372 /// Wrap a line of text at `self.width` characters. Strings are
373 /// wrapped based on their displayed width, not their size in
374 /// bytes.
375 ///
376 /// # Complexities
377 ///
378 /// This method simply collects the lines produced by `wrap_iter`.
379 /// As such, it inherits the O(*n*) overall time and memory
380 /// complexity where *n* is the input string length.
381 ///
382 /// # Examples
383 ///
384 /// ```
385 /// use textwrap::Wrapper;
386 ///
387 /// let wrap15 = Wrapper::new(15);
388 /// assert_eq!(wrap15.wrap("Concurrency without data races."),
389 /// vec!["Concurrency",
390 /// "without data",
391 /// "races."]);
392 ///
393 /// let wrap20 = Wrapper::new(20);
394 /// assert_eq!(wrap20.wrap("Concurrency without data races."),
395 /// vec!["Concurrency without",
396 /// "data races."]);
397 /// ```
398 pub fn wrap(&self, s: &'a str) -> Vec<Cow<'a, str>> {
399 self.wrap_iter(s).collect::<Vec<_>>()
400 }
401
402 /// Lazily wrap a line of text at `self.width` characters. Strings
403 /// are wrapped based on their displayed width, not their size in
404 /// bytes.
405 ///
406 /// The [`WordSplitter`] stored in [`self.splitter`] is used
407 /// whenever when a word is too large to fit on the current line.
408 /// By changing the field, different hyphenation strategies can be
409 /// implemented.
410 ///
411 /// # Complexities
412 ///
413 /// This method returns a [`WrapIter`] iterator which borrows this
414 /// `Wrapper`. The algorithm used has a linear complexity, so
415 /// getting the next line from the iterator will take O(*w*) time,
416 /// where *w* is the wrapping width. Fully processing the iterator
417 /// will take O(*n*) time for an input string of length *n*.
418 ///
419 /// When no indentation is used, each line returned is a slice of
420 /// the input string and the memory overhead is thus constant.
421 /// Otherwise new memory is allocated for each line returned.
422 ///
423 /// # Examples
424 ///
425 /// ```
426 /// use std::borrow::Cow;
427 /// use textwrap::Wrapper;
428 ///
429 /// let wrap20 = Wrapper::new(20);
430 /// let mut wrap20_iter = wrap20.wrap_iter("Zero-cost abstractions.");
431 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost")));
432 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions.")));
433 /// assert_eq!(wrap20_iter.next(), None);
434 ///
435 /// let wrap25 = Wrapper::new(25);
436 /// let mut wrap25_iter = wrap25.wrap_iter("Zero-cost abstractions.");
437 /// assert_eq!(wrap25_iter.next(), Some(Cow::from("Zero-cost abstractions.")));
438 /// assert_eq!(wrap25_iter.next(), None);
439 /// ```
440 ///
441 /// [`self.splitter`]: #structfield.splitter
442 /// [`WordSplitter`]: trait.WordSplitter.html
443 /// [`WrapIter`]: struct.WrapIter.html
444 pub fn wrap_iter(&'w self, s: &'a str) -> WrapIter<'w, 'a, S> {
445 WrapIter {
446 wrapper: self,
447 wrap_iter_impl: WrapIterImpl::new(self, s),
448 }
449 }
450
451 /// Lazily wrap a line of text at `self.width` characters. Strings
452 /// are wrapped based on their displayed width, not their size in
453 /// bytes.
454 ///
455 /// The [`WordSplitter`] stored in [`self.splitter`] is used
456 /// whenever when a word is too large to fit on the current line.
457 /// By changing the field, different hyphenation strategies can be
458 /// implemented.
459 ///
460 /// # Complexities
461 ///
462 /// This method consumes the `Wrapper` and returns a
463 /// [`IntoWrapIter`] iterator. Fully processing the iterator has
464 /// the same O(*n*) time complexity as [`wrap_iter`], where *n* is
465 /// the length of the input string.
466 ///
467 /// # Examples
468 ///
469 /// ```
470 /// use std::borrow::Cow;
471 /// use textwrap::Wrapper;
472 ///
473 /// let wrap20 = Wrapper::new(20);
474 /// let mut wrap20_iter = wrap20.into_wrap_iter("Zero-cost abstractions.");
475 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost")));
476 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions.")));
477 /// assert_eq!(wrap20_iter.next(), None);
478 /// ```
479 ///
480 /// [`self.splitter`]: #structfield.splitter
481 /// [`WordSplitter`]: trait.WordSplitter.html
482 /// [`IntoWrapIter`]: struct.IntoWrapIter.html
483 /// [`wrap_iter`]: #method.wrap_iter
484 pub fn into_wrap_iter(self, s: &'a str) -> IntoWrapIter<'a, S> {
485 let wrap_iter_impl = WrapIterImpl::new(&self, s);
486
487 IntoWrapIter {
488 wrapper: self,
489 wrap_iter_impl: wrap_iter_impl,
490 }
491 }
492 }
493
494
495 /// An iterator over the lines of the input string which owns a
496 /// `Wrapper`. An instance of `IntoWrapIter` is typically obtained
497 /// through either [`wrap_iter`] or [`Wrapper::into_wrap_iter`].
498 ///
499 /// Each call of `.next()` method yields a line wrapped in `Some` if the
500 /// input hasn't been fully processed yet. Otherwise it returns `None`.
501 ///
502 /// [`wrap_iter`]: fn.wrap_iter.html
503 /// [`Wrapper::into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter
504 pub struct IntoWrapIter<'a, S: WordSplitter> {
505 wrapper: Wrapper<'a, S>,
506 wrap_iter_impl: WrapIterImpl<'a>,
507 }
508
509 impl<'a, S: WordSplitter> Iterator for IntoWrapIter<'a, S> {
510 type Item = Cow<'a, str>;
511
512 fn next(&mut self) -> Option<Cow<'a, str>> {
513 self.wrap_iter_impl.impl_next(&self.wrapper)
514 }
515 }
516
517 /// An iterator over the lines of the input string which borrows a
518 /// `Wrapper`. An instance of `WrapIter` is typically obtained
519 /// through the [`Wrapper::wrap_iter`] method.
520 ///
521 /// Each call of `.next()` method yields a line wrapped in `Some` if the
522 /// input hasn't been fully processed yet. Otherwise it returns `None`.
523 ///
524 /// [`Wrapper::wrap_iter`]: struct.Wrapper.html#method.wrap_iter
525 pub struct WrapIter<'w, 'a: 'w, S: WordSplitter + 'w> {
526 wrapper: &'w Wrapper<'a, S>,
527 wrap_iter_impl: WrapIterImpl<'a>,
528 }
529
530 impl<'w, 'a: 'w, S: WordSplitter> Iterator for WrapIter<'w, 'a, S> {
531 type Item = Cow<'a, str>;
532
533 fn next(&mut self) -> Option<Cow<'a, str>> {
534 self.wrap_iter_impl.impl_next(self.wrapper)
535 }
536 }
537
538 struct WrapIterImpl<'a> {
539 // String to wrap.
540 source: &'a str,
541 // CharIndices iterator over self.source.
542 char_indices: CharIndices<'a>,
543 // Is the next element the first one ever produced?
544 is_next_first: bool,
545 // Byte index where the current line starts.
546 start: usize,
547 // Byte index of the last place where the string can be split.
548 split: usize,
549 // Size in bytes of the character at self.source[self.split].
550 split_len: usize,
551 // Width of self.source[self.start..idx].
552 line_width: usize,
553 // Width of self.source[self.start..self.split].
554 line_width_at_split: usize,
555 // Tracking runs of whitespace characters.
556 in_whitespace: bool,
557 // Has iterator finished producing elements?
558 finished: bool,
559 }
560
561 impl<'a> WrapIterImpl<'a> {
562 fn new<S: WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a> {
563 WrapIterImpl {
564 source: s,
565 char_indices: s.char_indices(),
566 is_next_first: true,
567 start: 0,
568 split: 0,
569 split_len: 0,
570 line_width: wrapper.initial_indent.width(),
571 line_width_at_split: wrapper.initial_indent.width(),
572 in_whitespace: false,
573 finished: false,
574 }
575 }
576
577 fn create_result_line<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str> {
578 if self.is_next_first {
579 self.is_next_first = false;
580 Cow::from(wrapper.initial_indent)
581 } else {
582 Cow::from(wrapper.subsequent_indent)
583 }
584 }
585
586 fn impl_next<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>> {
587 if self.finished {
588 return None;
589 }
590
591 while let Some((idx, ch)) = self.char_indices.next() {
592 let char_width = ch.width().unwrap_or(0);
593 let char_len = ch.len_utf8();
594 if ch.is_whitespace() && ch != NBSP {
595 // Extend the previous split or create a new one.
596 if self.in_whitespace {
597 self.split_len += char_len;
598 } else {
599 self.split = idx;
600 self.split_len = char_len;
601 }
602 self.line_width_at_split = self.line_width + char_width;
603 self.in_whitespace = true;
604 } else if self.line_width + char_width > wrapper.width {
605 // There is no room for this character on the current
606 // line. Try to split the final word.
607 let remaining_text = &self.source[self.split + self.split_len..];
608 let final_word = match remaining_text
609 .find(|ch: char| ch.is_whitespace() && ch != NBSP) {
610 Some(i) => &remaining_text[..i],
611 None => remaining_text,
612 };
613
614 let mut hyphen = "";
615 let splits = wrapper.splitter.split(final_word);
616 for &(head, hyp, _) in splits.iter().rev() {
617 if self.line_width_at_split + head.width() + hyp.width() <= wrapper.width {
618 self.split += head.len();
619 self.split_len = 0;
620 hyphen = hyp;
621 break;
622 }
623 }
624
625 if self.start >= self.split {
626 // The word is too big to fit on a single line, so we
627 // need to split it at the current index.
628 if wrapper.break_words {
629 // Break work at current index.
630 self.split = idx;
631 self.split_len = 0;
632 self.line_width_at_split = self.line_width;
633 } else {
634 // Add smallest split.
635 self.split = self.start + splits[0].0.len();
636 self.split_len = 0;
637 self.line_width_at_split = self.line_width;
638 }
639 }
640
641 if self.start < self.split {
642 let mut result_line = self.create_result_line(wrapper);
643 cow_add_assign(&mut result_line, &self.source[self.start..self.split]);
644 cow_add_assign(&mut result_line, hyphen);
645
646 self.start = self.split + self.split_len;
647 self.line_width += wrapper.subsequent_indent.width();
648 self.line_width -= self.line_width_at_split;
649 self.line_width += char_width;
650
651 return Some(result_line);
652 }
653 } else {
654 self.in_whitespace = false;
655 }
656 self.line_width += char_width;
657 }
658
659 // Add final line.
660 let final_line = if self.start < self.source.len() {
661 let mut result_line = self.create_result_line(wrapper);
662 cow_add_assign(&mut result_line, &self.source[self.start..]);
663
664 Some(result_line)
665 } else {
666 None
667 };
668
669 self.finished = true;
670
671 final_line
672 }
673 }
674
675
676 /// Return the current terminal width. If the terminal width cannot be
677 /// determined (typically because the standard output is not connected
678 /// to a terminal), a default width of 80 characters will be used.
679 ///
680 /// # Examples
681 ///
682 /// Create a `Wrapper` for the current terminal with a two column
683 /// margin:
684 ///
685 /// ```no_run
686 /// use textwrap::{Wrapper, NoHyphenation, termwidth};
687 ///
688 /// let width = termwidth() - 4; // Two columns on each side.
689 /// let wrapper = Wrapper::with_splitter(width, NoHyphenation)
690 /// .initial_indent(" ")
691 /// .subsequent_indent(" ");
692 /// ```
693 pub fn termwidth() -> usize {
694 term_size::dimensions_stdout().map_or(80, |(w, _)| w)
695 }
696
697 /// Fill a line of text at `width` characters. Strings are wrapped
698 /// based on their displayed width, not their size in bytes.
699 ///
700 /// The result is a string with newlines between each line. Use
701 /// [`wrap`] if you need access to the individual lines or
702 /// [`wrap_iter`] for its iterator counterpart.
703 ///
704 /// ```
705 /// use textwrap::fill;
706 ///
707 /// assert_eq!(fill("Memory safety without garbage collection.", 15),
708 /// "Memory safety\nwithout garbage\ncollection.");
709 /// ```
710 ///
711 /// This function creates a Wrapper on the fly with default settings.
712 /// If you need to set a language corpus for automatic hyphenation, or
713 /// need to fill many strings, then it is suggested to create Wrapper
714 /// and call its [`fill` method].
715 ///
716 /// [`wrap`]: fn.wrap.html
717 /// [`wrap_iter`]: fn.wrap_iter.html
718 /// [`fill` method]: struct.Wrapper.html#method.fill
719 pub fn fill(s: &str, width: usize) -> String {
720 Wrapper::new(width).fill(s)
721 }
722
723 /// Wrap a line of text at `width` characters. Strings are wrapped
724 /// based on their displayed width, not their size in bytes.
725 ///
726 /// This function creates a Wrapper on the fly with default settings.
727 /// If you need to set a language corpus for automatic hyphenation, or
728 /// need to wrap many strings, then it is suggested to create Wrapper
729 /// and call its [`wrap` method].
730 ///
731 /// The result is a vector of strings. Use [`wrap_iter`] if you need an
732 /// iterator version.
733 ///
734 /// # Examples
735 ///
736 /// ```
737 /// use textwrap::wrap;
738 ///
739 /// assert_eq!(wrap("Concurrency without data races.", 15),
740 /// vec!["Concurrency",
741 /// "without data",
742 /// "races."]);
743 ///
744 /// assert_eq!(wrap("Concurrency without data races.", 20),
745 /// vec!["Concurrency without",
746 /// "data races."]);
747 /// ```
748 ///
749 /// [`wrap_iter`]: fn.wrap_iter.html
750 /// [`wrap` method]: struct.Wrapper.html#method.wrap
751 pub fn wrap(s: &str, width: usize) -> Vec<Cow<str>> {
752 Wrapper::new(width).wrap(s)
753 }
754
755 /// Lazily wrap a line of text at `self.width` characters. Strings are
756 /// wrapped based on their displayed width, not their size in bytes.
757 ///
758 /// This function creates a Wrapper on the fly with default settings.
759 /// It then calls the [`into_wrap_iter`] method. Hence, the return
760 /// value is an [`IntoWrapIter`], not a [`WrapIter`] as the function
761 /// name would otherwise suggest.
762 ///
763 /// If you need to set a language corpus for automatic hyphenation, or
764 /// need to wrap many strings, then it is suggested to create Wrapper
765 /// and call its [`wrap_iter`] or [`into_wrap_iter`] methods.
766 ///
767 /// # Examples
768 ///
769 /// ```
770 /// use std::borrow::Cow;
771 /// use textwrap::wrap_iter;
772 ///
773 /// let mut wrap20_iter = wrap_iter("Zero-cost abstractions.", 20);
774 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost")));
775 /// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions.")));
776 /// assert_eq!(wrap20_iter.next(), None);
777 ///
778 /// let mut wrap25_iter = wrap_iter("Zero-cost abstractions.", 25);
779 /// assert_eq!(wrap25_iter.next(), Some(Cow::from("Zero-cost abstractions.")));
780 /// assert_eq!(wrap25_iter.next(), None);
781 /// ```
782 ///
783 /// [`wrap_iter`]: struct.Wrapper.html#method.wrap_iter
784 /// [`into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter
785 /// [`IntoWrapIter`]: struct.IntoWrapIter.html
786 /// [`WrapIter`]: struct.WrapIter.html
787 pub fn wrap_iter<'s>(s: &'s str, width: usize) -> IntoWrapIter<'s, HyphenSplitter> {
788 Wrapper::new(width).into_wrap_iter(s)
789 }
790
791 /// Add prefix to each non-empty line.
792 ///
793 /// ```
794 /// use textwrap::indent;
795 ///
796 /// assert_eq!(indent("Foo\nBar\n", " "), " Foo\n Bar\n");
797 /// ```
798 ///
799 /// Empty lines (lines consisting only of whitespace) are not indented
800 /// and the whitespace is replaced by a single newline (`\n`):
801 ///
802 /// ```
803 /// use textwrap::indent;
804 ///
805 /// assert_eq!(indent("Foo\n\nBar\n \t \nBaz\n", " "),
806 /// " Foo\n\n Bar\n\n Baz\n");
807 /// ```
808 ///
809 /// Leading and trailing whitespace on non-empty lines is kept
810 /// unchanged:
811 ///
812 /// ```
813 /// use textwrap::indent;
814 ///
815 /// assert_eq!(indent(" \t Foo ", " "), " \t Foo \n");
816 /// ```
817 pub fn indent(s: &str, prefix: &str) -> String {
818 let mut result = String::new();
819 for line in s.lines() {
820 if line.chars().any(|c| !c.is_whitespace()) {
821 result.push_str(prefix);
822 result.push_str(line);
823 }
824 result.push('\n');
825 }
826 result
827 }
828
829 /// Removes common leading whitespace from each line.
830 ///
831 /// This will look at each non-empty line and determine the maximum
832 /// amount of whitespace that can be removed from the line.
833 ///
834 /// ```
835 /// use textwrap::dedent;
836 ///
837 /// assert_eq!(dedent(" 1st line\n 2nd line\n"),
838 /// "1st line\n2nd line\n");
839 /// ```
840 pub fn dedent(s: &str) -> String {
841 let mut prefix = String::new();
842 let mut lines = s.lines();
843
844 // We first search for a non-empty line to find a prefix.
845 for line in &mut lines {
846 let whitespace = line.chars()
847 .take_while(|c| c.is_whitespace())
848 .collect::<String>();
849 // Check if the line had anything but whitespace
850 if whitespace.len() < line.len() {
851 prefix = whitespace;
852 break;
853 }
854 }
855
856 // We then continue looking through the remaining lines to
857 // possibly shorten the prefix.
858 for line in &mut lines {
859 let whitespace = line.chars()
860 .zip(prefix.chars())
861 .take_while(|&(a, b)| a == b)
862 .map(|(_, b)| b)
863 .collect::<String>();
864 // Check if we have found a shorter prefix
865 if whitespace.len() < prefix.len() {
866 prefix = whitespace;
867 }
868 }
869
870 // We now go over the lines a second time to build the result.
871 let mut result = String::new();
872 for line in s.lines() {
873 if line.starts_with(&prefix) && line.chars().any(|c| !c.is_whitespace()) {
874 let (_, tail) = line.split_at(prefix.len());
875 result.push_str(tail);
876 }
877 result.push('\n');
878 }
879 result
880 }
881
882 #[cfg(test)]
883 mod tests {
884 #[cfg(feature = "hyphenation")]
885 extern crate hyphenation;
886
887 #[cfg(feature = "hyphenation")]
888 use hyphenation::Language;
889 use super::*;
890
891 /// Add newlines. Ensures that the final line in the vector also
892 /// has a newline.
893 fn add_nl(lines: &Vec<&str>) -> String {
894 lines.join("\n") + "\n"
895 }
896
897 #[test]
898 fn no_wrap() {
899 assert_eq!(wrap("foo", 10), vec!["foo"]);
900 }
901
902 #[test]
903 fn simple() {
904 assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]);
905 }
906
907 #[test]
908 fn multi_word_on_line() {
909 assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]);
910 }
911
912 #[test]
913 fn long_word() {
914 assert_eq!(wrap("foo", 0), vec!["f", "o", "o"]);
915 }
916
917 #[test]
918 fn long_words() {
919 assert_eq!(wrap("foo bar", 0), vec!["f", "o", "o", "b", "a", "r"]);
920 }
921
922 #[test]
923 fn leading_whitespace() {
924 assert_eq!(wrap(" foo bar", 6), vec![" foo", "bar"]);
925 }
926
927 #[test]
928 fn trailing_whitespace() {
929 assert_eq!(wrap("foo bar ", 6), vec!["foo", "bar "]);
930 }
931
932 #[test]
933 fn interior_whitespace() {
934 assert_eq!(wrap("foo: bar baz", 10), vec!["foo: bar", "baz"]);
935 }
936
937 #[test]
938 fn extra_whitespace_start_of_line() {
939 // Whitespace is only significant inside a line. After a line
940 // gets too long and is broken, the first word starts in
941 // column zero and is not indented. The line before might end
942 // up with trailing whitespace.
943 assert_eq!(wrap("foo bar", 5), vec!["foo", "bar"]);
944 }
945
946 #[test]
947 fn wide_character_handling() {
948 assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]);
949 assert_eq!(wrap("Hello, World!", 15),
950 vec!["Hello,", "World!"]);
951 }
952
953 #[test]
954 fn indent_empty() {
955 let wrapper = Wrapper::new(10).initial_indent("!!!");
956 assert_eq!(wrapper.fill(""), "");
957 }
958
959 #[test]
960 fn indent_single_line() {
961 let wrapper = Wrapper::new(10).initial_indent(">>>"); // No trailing space
962 assert_eq!(wrapper.fill("foo"), ">>>foo");
963 }
964
965 #[test]
966 fn indent_multiple_lines() {
967 let wrapper = Wrapper::new(6).initial_indent("* ").subsequent_indent(" ");
968 assert_eq!(wrapper.wrap("foo bar baz"), vec!["* foo", " bar", " baz"]);
969 }
970
971 #[test]
972 fn indent_break_words() {
973 let wrapper = Wrapper::new(5).initial_indent("* ").subsequent_indent(" ");
974 assert_eq!(wrapper.wrap("foobarbaz"), vec!["* foo", " bar", " baz"]);
975 }
976
977 #[test]
978 fn hyphens() {
979 assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]);
980 }
981
982 #[test]
983 fn trailing_hyphen() {
984 let wrapper = Wrapper::new(5).break_words(false);
985 assert_eq!(wrapper.wrap("foobar-"), vec!["foobar-"]);
986 }
987
988 #[test]
989 fn multiple_hyphens() {
990 assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]);
991 }
992
993 #[test]
994 fn hyphens_flag() {
995 let wrapper = Wrapper::new(5).break_words(false);
996 assert_eq!(wrapper.wrap("The --foo-bar flag."),
997 vec!["The", "--foo-", "bar", "flag."]);
998 }
999
1000 #[test]
1001 fn repeated_hyphens() {
1002 let wrapper = Wrapper::new(4).break_words(false);
1003 assert_eq!(wrapper.wrap("foo--bar"), vec!["foo--bar"]);
1004 }
1005
1006 #[test]
1007 fn hyphens_alphanumeric() {
1008 assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]);
1009 }
1010
1011 #[test]
1012 fn hyphens_non_alphanumeric() {
1013 let wrapper = Wrapper::new(5).break_words(false);
1014 assert_eq!(wrapper.wrap("foo(-)bar"), vec!["foo(-)bar"]);
1015 }
1016
1017 #[test]
1018 fn multiple_splits() {
1019 assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]);
1020 }
1021
1022 #[test]
1023 fn forced_split() {
1024 let wrapper = Wrapper::new(5).break_words(false);
1025 assert_eq!(wrapper.wrap("foobar-baz"), vec!["foobar-", "baz"]);
1026 }
1027
1028 #[test]
1029 fn no_hyphenation() {
1030 let wrapper = Wrapper::with_splitter(8, NoHyphenation);
1031 assert_eq!(wrapper.wrap("foo bar-baz"), vec!["foo", "bar-baz"]);
1032 }
1033
1034 #[test]
1035 #[cfg(feature = "hyphenation")]
1036 fn auto_hyphenation() {
1037 let corpus = hyphenation::load(Language::English_US).unwrap();
1038 let wrapper = Wrapper::new(10);
1039 assert_eq!(wrapper.wrap("Internationalization"),
1040 vec!["Internatio", "nalization"]);
1041
1042 let wrapper = Wrapper::with_splitter(10, corpus);
1043 assert_eq!(wrapper.wrap("Internationalization"),
1044 vec!["Interna-", "tionaliza-", "tion"]);
1045 }
1046
1047 #[test]
1048 #[cfg(feature = "hyphenation")]
1049 fn borrowed_lines() {
1050 // Lines that end with an extra hyphen are owned, the final
1051 // line is borrowed.
1052 use std::borrow::Cow::{Borrowed, Owned};
1053 let corpus = hyphenation::load(Language::English_US).unwrap();
1054 let wrapper = Wrapper::with_splitter(10, corpus);
1055 let lines = wrapper.wrap("Internationalization");
1056 if let Borrowed(s) = lines[0] {
1057 assert!(false, "should not have been borrowed: {:?}", s);
1058 }
1059 if let Borrowed(s) = lines[1] {
1060 assert!(false, "should not have been borrowed: {:?}", s);
1061 }
1062 if let Owned(ref s) = lines[2] {
1063 assert!(false, "should not have been owned: {:?}", s);
1064 }
1065 }
1066
1067 #[test]
1068 #[cfg(feature = "hyphenation")]
1069 fn auto_hyphenation_with_hyphen() {
1070 let corpus = hyphenation::load(Language::English_US).unwrap();
1071 let wrapper = Wrapper::new(8).break_words(false);
1072 assert_eq!(wrapper.wrap("over-caffinated"), vec!["over-", "caffinated"]);
1073
1074 let wrapper = Wrapper::with_splitter(8, corpus).break_words(false);
1075 assert_eq!(wrapper.wrap("over-caffinated"),
1076 vec!["over-", "caffi-", "nated"]);
1077 }
1078
1079 #[test]
1080 fn break_words() {
1081 assert_eq!(wrap("foobarbaz", 3), vec!["foo", "bar", "baz"]);
1082 }
1083
1084 #[test]
1085 fn break_words_wide_characters() {
1086 assert_eq!(wrap("Hello", 5), vec!["He", "ll", "o"]);
1087 }
1088
1089 #[test]
1090 fn break_words_zero_width() {
1091 assert_eq!(wrap("foobar", 0), vec!["f", "o", "o", "b", "a", "r"]);
1092 }
1093
1094 #[test]
1095 fn test_non_breaking_space() {
1096 let wrapper = Wrapper::new(5).break_words(false);
1097 assert_eq!(wrapper.fill("foo bar baz"), "foo bar baz");
1098 }
1099
1100 #[test]
1101 fn test_non_breaking_hyphen() {
1102 let wrapper = Wrapper::new(5).break_words(false);
1103 assert_eq!(wrapper.fill("foo‑bar‑baz"), "foo‑bar‑baz");
1104 }
1105
1106 #[test]
1107 fn test_fill() {
1108 assert_eq!(fill("foo bar baz", 10), "foo bar\nbaz");
1109 }
1110
1111 #[test]
1112 fn test_indent_empty() {
1113 assert_eq!(indent("\n", " "), "\n");
1114 }
1115
1116 #[test]
1117 #[cfg_attr(rustfmt, rustfmt_skip)]
1118 fn test_indent_nonempty() {
1119 let x = vec![" foo",
1120 "bar",
1121 " baz"];
1122 let y = vec!["// foo",
1123 "//bar",
1124 "// baz"];
1125 assert_eq!(indent(&add_nl(&x), "//"), add_nl(&y));
1126 }
1127
1128 #[test]
1129 #[cfg_attr(rustfmt, rustfmt_skip)]
1130 fn test_indent_empty_line() {
1131 let x = vec![" foo",
1132 "bar",
1133 "",
1134 " baz"];
1135 let y = vec!["// foo",
1136 "//bar",
1137 "",
1138 "// baz"];
1139 assert_eq!(indent(&add_nl(&x), "//"), add_nl(&y));
1140 }
1141
1142 #[test]
1143 fn test_dedent_empty() {
1144 assert_eq!(dedent(""), "");
1145 }
1146
1147 #[test]
1148 #[cfg_attr(rustfmt, rustfmt_skip)]
1149 fn test_dedent_multi_line() {
1150 let x = vec![" foo",
1151 " bar",
1152 " baz"];
1153 let y = vec![" foo",
1154 "bar",
1155 " baz"];
1156 assert_eq!(dedent(&add_nl(&x)), add_nl(&y));
1157 }
1158
1159 #[test]
1160 #[cfg_attr(rustfmt, rustfmt_skip)]
1161 fn test_dedent_empty_line() {
1162 let x = vec![" foo",
1163 " bar",
1164 " ",
1165 " baz"];
1166 let y = vec![" foo",
1167 "bar",
1168 "",
1169 " baz"];
1170 assert_eq!(dedent(&add_nl(&x)), add_nl(&y));
1171 }
1172
1173 #[test]
1174 #[cfg_attr(rustfmt, rustfmt_skip)]
1175 fn test_dedent_mixed_whitespace() {
1176 let x = vec!["\tfoo",
1177 " bar"];
1178 let y = vec!["\tfoo",
1179 " bar"];
1180 assert_eq!(dedent(&add_nl(&x)), add_nl(&y));
1181 }
1182 }