diff options
Diffstat (limited to 'textwrap/src/lib.rs')
-rw-r--r-- | textwrap/src/lib.rs | 987 |
1 files changed, 0 insertions, 987 deletions
diff --git a/textwrap/src/lib.rs b/textwrap/src/lib.rs deleted file mode 100644 index 2f82325..0000000 --- a/textwrap/src/lib.rs +++ /dev/null @@ -1,987 +0,0 @@ -//! `textwrap` provides functions for word wrapping and filling text. -//! -//! Wrapping text can be very useful in commandline programs where you -//! want to format dynamic output nicely so it looks good in a -//! terminal. A quick example: -//! -//! ```no_run -//! extern crate textwrap; -//! use textwrap::fill; -//! -//! fn main() { -//! let text = "textwrap: a small library for wrapping text."; -//! println!("{}", fill(text, 18)); -//! } -//! ``` -//! -//! This will display the following output: -//! -//! ```text -//! textwrap: a small -//! library for -//! wrapping text. -//! ``` -//! -//! # Displayed Width vs Byte Size -//! -//! To word wrap text, one must know the width of each word so one can -//! know when to break lines. This library measures the width of text -//! using the [displayed width][unicode-width], not the size in bytes. -//! -//! This is important for non-ASCII text. ASCII characters such as `a` -//! and `!` are simple and take up one column each. This means that -//! the displayed width is equal to the string length in bytes. -//! However, non-ASCII characters and symbols take up more than one -//! byte when UTF-8 encoded: `é` is `0xc3 0xa9` (two bytes) and `⚙` is -//! `0xe2 0x9a 0x99` (three bytes) in UTF-8, respectively. -//! -//! This is why we take care to use the displayed width instead of the -//! byte count when computing line lengths. All functions in this -//! library handle Unicode characters like this. -//! -//! [unicode-width]: https://docs.rs/unicode-width/ - -#![doc(html_root_url = "https://docs.rs/textwrap/0.11.0")] -#![deny(missing_docs)] -#![deny(missing_debug_implementations)] - -#[cfg(feature = "hyphenation")] -extern crate hyphenation; -#[cfg(feature = "term_size")] -extern crate term_size; -extern crate unicode_width; - -use std::borrow::Cow; -use std::str::CharIndices; - -use unicode_width::UnicodeWidthChar; -use unicode_width::UnicodeWidthStr; - -/// A non-breaking space. -const NBSP: char = '\u{a0}'; - -mod indentation; -pub use indentation::dedent; -pub use indentation::indent; - -mod splitting; -pub use splitting::{HyphenSplitter, NoHyphenation, WordSplitter}; - -/// A Wrapper holds settings for wrapping and filling text. Use it -/// when the convenience [`wrap_iter`], [`wrap`] and [`fill`] functions -/// are not flexible enough. -/// -/// [`wrap_iter`]: fn.wrap_iter.html -/// [`wrap`]: fn.wrap.html -/// [`fill`]: fn.fill.html -/// -/// The algorithm used by the `WrapIter` iterator (returned from the -/// `wrap_iter` method) works by doing successive partial scans over -/// words in the input string (where each single scan yields a single -/// line) so that the overall time and memory complexity is O(*n*) where -/// *n* is the length of the input string. -#[derive(Clone, Debug)] -pub struct Wrapper<'a, S: WordSplitter> { - /// The width in columns at which the text will be wrapped. - pub width: usize, - /// Indentation used for the first line of output. - pub initial_indent: &'a str, - /// Indentation used for subsequent lines of output. - pub subsequent_indent: &'a str, - /// Allow long words to be broken if they cannot fit on a line. - /// When set to `false`, some lines may be longer than - /// `self.width`. - pub break_words: bool, - /// The method for splitting words. If the `hyphenation` feature - /// is enabled, you can use a `hyphenation::Standard` dictionary - /// here to get language-aware hyphenation. - pub splitter: S, -} - -impl<'a> Wrapper<'a, HyphenSplitter> { - /// Create a new Wrapper for wrapping at the specified width. By - /// default, we allow words longer than `width` to be broken. A - /// [`HyphenSplitter`] will be used by default for splitting - /// words. See the [`WordSplitter`] trait for other options. - /// - /// [`HyphenSplitter`]: struct.HyphenSplitter.html - /// [`WordSplitter`]: trait.WordSplitter.html - pub fn new(width: usize) -> Wrapper<'a, HyphenSplitter> { - Wrapper::with_splitter(width, HyphenSplitter) - } - - /// Create a new Wrapper for wrapping text at the current terminal - /// width. If the terminal width cannot be determined (typically - /// because the standard input and output is not connected to a - /// terminal), a width of 80 characters will be used. Other - /// settings use the same defaults as `Wrapper::new`. - /// - /// Equivalent to: - /// - /// ```no_run - /// # #![allow(unused_variables)] - /// use textwrap::{Wrapper, termwidth}; - /// - /// let wrapper = Wrapper::new(termwidth()); - /// ``` - #[cfg(feature = "term_size")] - pub fn with_termwidth() -> Wrapper<'a, HyphenSplitter> { - Wrapper::new(termwidth()) - } -} - -impl<'a, S: WordSplitter> Wrapper<'a, S> { - /// Use the given [`WordSplitter`] to create a new Wrapper for - /// wrapping at the specified width. By default, we allow words - /// longer than `width` to be broken. - /// - /// [`WordSplitter`]: trait.WordSplitter.html - pub fn with_splitter(width: usize, splitter: S) -> Wrapper<'a, S> { - Wrapper { - width: width, - initial_indent: "", - subsequent_indent: "", - break_words: true, - splitter: splitter, - } - } - - /// Change [`self.initial_indent`]. The initial indentation is - /// used on the very first line of output. - /// - /// # Examples - /// - /// Classic paragraph indentation can be achieved by specifying an - /// initial indentation and wrapping each paragraph by itself: - /// - /// ```no_run - /// # #![allow(unused_variables)] - /// use textwrap::Wrapper; - /// - /// let wrapper = Wrapper::new(15).initial_indent(" "); - /// ``` - /// - /// [`self.initial_indent`]: #structfield.initial_indent - pub fn initial_indent(self, indent: &'a str) -> Wrapper<'a, S> { - Wrapper { - initial_indent: indent, - ..self - } - } - - /// Change [`self.subsequent_indent`]. The subsequent indentation - /// is used on lines following the first line of output. - /// - /// # Examples - /// - /// Combining initial and subsequent indentation lets you format a - /// single paragraph as a bullet list: - /// - /// ```no_run - /// # #![allow(unused_variables)] - /// use textwrap::Wrapper; - /// - /// let wrapper = Wrapper::new(15) - /// .initial_indent("* ") - /// .subsequent_indent(" "); - /// ``` - /// - /// [`self.subsequent_indent`]: #structfield.subsequent_indent - pub fn subsequent_indent(self, indent: &'a str) -> Wrapper<'a, S> { - Wrapper { - subsequent_indent: indent, - ..self - } - } - - /// Change [`self.break_words`]. This controls if words longer - /// than `self.width` can be broken, or if they will be left - /// sticking out into the right margin. - /// - /// [`self.break_words`]: #structfield.break_words - pub fn break_words(self, setting: bool) -> Wrapper<'a, S> { - Wrapper { - break_words: setting, - ..self - } - } - - /// Fill a line of text at `self.width` characters. Strings are - /// wrapped based on their displayed width, not their size in - /// bytes. - /// - /// The result is a string with newlines between each line. Use - /// the `wrap` method if you need access to the individual lines. - /// - /// # Complexities - /// - /// This method simply joins the lines produced by `wrap_iter`. As - /// such, it inherits the O(*n*) time and memory complexity where - /// *n* is the input string length. - /// - /// # Examples - /// - /// ``` - /// use textwrap::Wrapper; - /// - /// let wrapper = Wrapper::new(15); - /// assert_eq!(wrapper.fill("Memory safety without garbage collection."), - /// "Memory safety\nwithout garbage\ncollection."); - /// ``` - pub fn fill(&self, s: &str) -> String { - // This will avoid reallocation in simple cases (no - // indentation, no hyphenation). - let mut result = String::with_capacity(s.len()); - - for (i, line) in self.wrap_iter(s).enumerate() { - if i > 0 { - result.push('\n'); - } - result.push_str(&line); - } - - result - } - - /// Wrap a line of text at `self.width` characters. Strings are - /// wrapped based on their displayed width, not their size in - /// bytes. - /// - /// # Complexities - /// - /// This method simply collects the lines produced by `wrap_iter`. - /// As such, it inherits the O(*n*) overall time and memory - /// complexity where *n* is the input string length. - /// - /// # Examples - /// - /// ``` - /// use textwrap::Wrapper; - /// - /// let wrap15 = Wrapper::new(15); - /// assert_eq!(wrap15.wrap("Concurrency without data races."), - /// vec!["Concurrency", - /// "without data", - /// "races."]); - /// - /// let wrap20 = Wrapper::new(20); - /// assert_eq!(wrap20.wrap("Concurrency without data races."), - /// vec!["Concurrency without", - /// "data races."]); - /// ``` - /// - /// Notice that newlines in the input are preserved. This means - /// that they force a line break, regardless of how long the - /// current line is: - /// - /// ``` - /// use textwrap::Wrapper; - /// - /// let wrapper = Wrapper::new(40); - /// assert_eq!(wrapper.wrap("First line.\nSecond line."), - /// vec!["First line.", "Second line."]); - /// ``` - /// - pub fn wrap(&self, s: &'a str) -> Vec<Cow<'a, str>> { - self.wrap_iter(s).collect::<Vec<_>>() - } - - /// Lazily wrap a line of text at `self.width` characters. Strings - /// are wrapped based on their displayed width, not their size in - /// bytes. - /// - /// The [`WordSplitter`] stored in [`self.splitter`] is used - /// whenever when a word is too large to fit on the current line. - /// By changing the field, different hyphenation strategies can be - /// implemented. - /// - /// # Complexities - /// - /// This method returns a [`WrapIter`] iterator which borrows this - /// `Wrapper`. The algorithm used has a linear complexity, so - /// getting the next line from the iterator will take O(*w*) time, - /// where *w* is the wrapping width. Fully processing the iterator - /// will take O(*n*) time for an input string of length *n*. - /// - /// When no indentation is used, each line returned is a slice of - /// the input string and the memory overhead is thus constant. - /// Otherwise new memory is allocated for each line returned. - /// - /// # Examples - /// - /// ``` - /// use std::borrow::Cow; - /// use textwrap::Wrapper; - /// - /// let wrap20 = Wrapper::new(20); - /// let mut wrap20_iter = wrap20.wrap_iter("Zero-cost abstractions."); - /// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost"))); - /// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions."))); - /// assert_eq!(wrap20_iter.next(), None); - /// - /// let wrap25 = Wrapper::new(25); - /// let mut wrap25_iter = wrap25.wrap_iter("Zero-cost abstractions."); - /// assert_eq!(wrap25_iter.next(), Some(Cow::from("Zero-cost abstractions."))); - /// assert_eq!(wrap25_iter.next(), None); - /// ``` - /// - /// [`self.splitter`]: #structfield.splitter - /// [`WordSplitter`]: trait.WordSplitter.html - /// [`WrapIter`]: struct.WrapIter.html - pub fn wrap_iter<'w>(&'w self, s: &'a str) -> WrapIter<'w, 'a, S> { - WrapIter { - wrapper: self, - inner: WrapIterImpl::new(self, s), - } - } - - /// Lazily wrap a line of text at `self.width` characters. Strings - /// are wrapped based on their displayed width, not their size in - /// bytes. - /// - /// The [`WordSplitter`] stored in [`self.splitter`] is used - /// whenever when a word is too large to fit on the current line. - /// By changing the field, different hyphenation strategies can be - /// implemented. - /// - /// # Complexities - /// - /// This method consumes the `Wrapper` and returns a - /// [`IntoWrapIter`] iterator. Fully processing the iterator has - /// the same O(*n*) time complexity as [`wrap_iter`], where *n* is - /// the length of the input string. - /// - /// # Examples - /// - /// ``` - /// use std::borrow::Cow; - /// use textwrap::Wrapper; - /// - /// let wrap20 = Wrapper::new(20); - /// let mut wrap20_iter = wrap20.into_wrap_iter("Zero-cost abstractions."); - /// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost"))); - /// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions."))); - /// assert_eq!(wrap20_iter.next(), None); - /// ``` - /// - /// [`self.splitter`]: #structfield.splitter - /// [`WordSplitter`]: trait.WordSplitter.html - /// [`IntoWrapIter`]: struct.IntoWrapIter.html - /// [`wrap_iter`]: #method.wrap_iter - pub fn into_wrap_iter(self, s: &'a str) -> IntoWrapIter<'a, S> { - let inner = WrapIterImpl::new(&self, s); - - IntoWrapIter { - wrapper: self, - inner: inner, - } - } -} - -/// An iterator over the lines of the input string which owns a -/// `Wrapper`. An instance of `IntoWrapIter` is typically obtained -/// through either [`wrap_iter`] or [`Wrapper::into_wrap_iter`]. -/// -/// Each call of `.next()` method yields a line wrapped in `Some` if the -/// input hasn't been fully processed yet. Otherwise it returns `None`. -/// -/// [`wrap_iter`]: fn.wrap_iter.html -/// [`Wrapper::into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter -#[derive(Debug)] -pub struct IntoWrapIter<'a, S: WordSplitter> { - wrapper: Wrapper<'a, S>, - inner: WrapIterImpl<'a>, -} - -impl<'a, S: WordSplitter> Iterator for IntoWrapIter<'a, S> { - type Item = Cow<'a, str>; - - fn next(&mut self) -> Option<Cow<'a, str>> { - self.inner.next(&self.wrapper) - } -} - -/// An iterator over the lines of the input string which borrows a -/// `Wrapper`. An instance of `WrapIter` is typically obtained -/// through the [`Wrapper::wrap_iter`] method. -/// -/// Each call of `.next()` method yields a line wrapped in `Some` if the -/// input hasn't been fully processed yet. Otherwise it returns `None`. -/// -/// [`Wrapper::wrap_iter`]: struct.Wrapper.html#method.wrap_iter -#[derive(Debug)] -pub struct WrapIter<'w, 'a: 'w, S: WordSplitter + 'w> { - wrapper: &'w Wrapper<'a, S>, - inner: WrapIterImpl<'a>, -} - -impl<'w, 'a: 'w, S: WordSplitter> Iterator for WrapIter<'w, 'a, S> { - type Item = Cow<'a, str>; - - fn next(&mut self) -> Option<Cow<'a, str>> { - self.inner.next(self.wrapper) - } -} - -/// Like `char::is_whitespace`, but non-breaking spaces don't count. -#[inline] -fn is_whitespace(ch: char) -> bool { - ch.is_whitespace() && ch != NBSP -} - -/// Common implementation details for `WrapIter` and `IntoWrapIter`. -#[derive(Debug)] -struct WrapIterImpl<'a> { - // String to wrap. - source: &'a str, - // CharIndices iterator over self.source. - char_indices: CharIndices<'a>, - // Byte index where the current line starts. - start: usize, - // Byte index of the last place where the string can be split. - split: usize, - // Size in bytes of the character at self.source[self.split]. - split_len: usize, - // Width of self.source[self.start..idx]. - line_width: usize, - // Width of self.source[self.start..self.split]. - line_width_at_split: usize, - // Tracking runs of whitespace characters. - in_whitespace: bool, - // Has iterator finished producing elements? - finished: bool, -} - -impl<'a> WrapIterImpl<'a> { - fn new<S: WordSplitter>(wrapper: &Wrapper<'a, S>, s: &'a str) -> WrapIterImpl<'a> { - WrapIterImpl { - source: s, - char_indices: s.char_indices(), - start: 0, - split: 0, - split_len: 0, - line_width: wrapper.initial_indent.width(), - line_width_at_split: wrapper.initial_indent.width(), - in_whitespace: false, - finished: false, - } - } - - fn create_result_line<S: WordSplitter>(&self, wrapper: &Wrapper<'a, S>) -> Cow<'a, str> { - if self.start == 0 { - Cow::from(wrapper.initial_indent) - } else { - Cow::from(wrapper.subsequent_indent) - } - } - - fn next<S: WordSplitter>(&mut self, wrapper: &Wrapper<'a, S>) -> Option<Cow<'a, str>> { - if self.finished { - return None; - } - - while let Some((idx, ch)) = self.char_indices.next() { - let char_width = ch.width().unwrap_or(0); - let char_len = ch.len_utf8(); - - if ch == '\n' { - self.split = idx; - self.split_len = char_len; - self.line_width_at_split = self.line_width; - self.in_whitespace = false; - - // If this is not the final line, return the current line. Otherwise, - // we will return the line with its line break after exiting the loop - if self.split + self.split_len < self.source.len() { - let mut line = self.create_result_line(wrapper); - line += &self.source[self.start..self.split]; - - self.start = self.split + self.split_len; - self.line_width = wrapper.subsequent_indent.width(); - - return Some(line); - } - } else if is_whitespace(ch) { - // Extend the previous split or create a new one. - if self.in_whitespace { - self.split_len += char_len; - } else { - self.split = idx; - self.split_len = char_len; - } - self.line_width_at_split = self.line_width + char_width; - self.in_whitespace = true; - } else if self.line_width + char_width > wrapper.width { - // There is no room for this character on the current - // line. Try to split the final word. - self.in_whitespace = false; - let remaining_text = &self.source[self.split + self.split_len..]; - let final_word = match remaining_text.find(is_whitespace) { - Some(i) => &remaining_text[..i], - None => remaining_text, - }; - - let mut hyphen = ""; - let splits = wrapper.splitter.split(final_word); - for &(head, hyp, _) in splits.iter().rev() { - if self.line_width_at_split + head.width() + hyp.width() <= wrapper.width { - // We can fit head into the current line. - // Advance the split point by the width of the - // whitespace and the head length. - self.split += self.split_len + head.len(); - self.split_len = 0; - hyphen = hyp; - break; - } - } - - if self.start >= self.split { - // The word is too big to fit on a single line, so we - // need to split it at the current index. - if wrapper.break_words { - // Break work at current index. - self.split = idx; - self.split_len = 0; - self.line_width_at_split = self.line_width; - } else { - // Add smallest split. - self.split = self.start + splits[0].0.len(); - self.split_len = 0; - self.line_width_at_split = self.line_width; - } - } - - if self.start < self.split { - let mut line = self.create_result_line(wrapper); - line += &self.source[self.start..self.split]; - line += hyphen; - - self.start = self.split + self.split_len; - self.line_width += wrapper.subsequent_indent.width(); - self.line_width -= self.line_width_at_split; - self.line_width += char_width; - - return Some(line); - } - } else { - self.in_whitespace = false; - } - self.line_width += char_width; - } - - self.finished = true; - - // Add final line. - if self.start < self.source.len() { - let mut line = self.create_result_line(wrapper); - line += &self.source[self.start..]; - return Some(line); - } - - None - } -} - -/// Return the current terminal width. If the terminal width cannot be -/// determined (typically because the standard output is not connected -/// to a terminal), a default width of 80 characters will be used. -/// -/// # Examples -/// -/// Create a `Wrapper` for the current terminal with a two column -/// margin: -/// -/// ```no_run -/// # #![allow(unused_variables)] -/// use textwrap::{Wrapper, NoHyphenation, termwidth}; -/// -/// let width = termwidth() - 4; // Two columns on each side. -/// let wrapper = Wrapper::with_splitter(width, NoHyphenation) -/// .initial_indent(" ") -/// .subsequent_indent(" "); -/// ``` -#[cfg(feature = "term_size")] -pub fn termwidth() -> usize { - term_size::dimensions_stdout().map_or(80, |(w, _)| w) -} - -/// Fill a line of text at `width` characters. Strings are wrapped -/// based on their displayed width, not their size in bytes. -/// -/// The result is a string with newlines between each line. Use -/// [`wrap`] if you need access to the individual lines or -/// [`wrap_iter`] for its iterator counterpart. -/// -/// ``` -/// use textwrap::fill; -/// -/// assert_eq!(fill("Memory safety without garbage collection.", 15), -/// "Memory safety\nwithout garbage\ncollection."); -/// ``` -/// -/// This function creates a Wrapper on the fly with default settings. -/// If you need to set a language corpus for automatic hyphenation, or -/// need to fill many strings, then it is suggested to create a Wrapper -/// and call its [`fill` method]. -/// -/// [`wrap`]: fn.wrap.html -/// [`wrap_iter`]: fn.wrap_iter.html -/// [`fill` method]: struct.Wrapper.html#method.fill -pub fn fill(s: &str, width: usize) -> String { - Wrapper::new(width).fill(s) -} - -/// Wrap a line of text at `width` characters. Strings are wrapped -/// based on their displayed width, not their size in bytes. -/// -/// This function creates a Wrapper on the fly with default settings. -/// If you need to set a language corpus for automatic hyphenation, or -/// need to wrap many strings, then it is suggested to create a Wrapper -/// and call its [`wrap` method]. -/// -/// The result is a vector of strings. Use [`wrap_iter`] if you need an -/// iterator version. -/// -/// # Examples -/// -/// ``` -/// use textwrap::wrap; -/// -/// assert_eq!(wrap("Concurrency without data races.", 15), -/// vec!["Concurrency", -/// "without data", -/// "races."]); -/// -/// assert_eq!(wrap("Concurrency without data races.", 20), -/// vec!["Concurrency without", -/// "data races."]); -/// ``` -/// -/// [`wrap_iter`]: fn.wrap_iter.html -/// [`wrap` method]: struct.Wrapper.html#method.wrap -pub fn wrap(s: &str, width: usize) -> Vec<Cow<str>> { - Wrapper::new(width).wrap(s) -} - -/// Lazily wrap a line of text at `width` characters. Strings are -/// wrapped based on their displayed width, not their size in bytes. -/// -/// This function creates a Wrapper on the fly with default settings. -/// It then calls the [`into_wrap_iter`] method. Hence, the return -/// value is an [`IntoWrapIter`], not a [`WrapIter`] as the function -/// name would otherwise suggest. -/// -/// If you need to set a language corpus for automatic hyphenation, or -/// need to wrap many strings, then it is suggested to create a Wrapper -/// and call its [`wrap_iter`] or [`into_wrap_iter`] methods. -/// -/// # Examples -/// -/// ``` -/// use std::borrow::Cow; -/// use textwrap::wrap_iter; -/// -/// let mut wrap20_iter = wrap_iter("Zero-cost abstractions.", 20); -/// assert_eq!(wrap20_iter.next(), Some(Cow::from("Zero-cost"))); -/// assert_eq!(wrap20_iter.next(), Some(Cow::from("abstractions."))); -/// assert_eq!(wrap20_iter.next(), None); -/// -/// let mut wrap25_iter = wrap_iter("Zero-cost abstractions.", 25); -/// assert_eq!(wrap25_iter.next(), Some(Cow::from("Zero-cost abstractions."))); -/// assert_eq!(wrap25_iter.next(), None); -/// ``` -/// -/// [`wrap_iter`]: struct.Wrapper.html#method.wrap_iter -/// [`into_wrap_iter`]: struct.Wrapper.html#method.into_wrap_iter -/// [`IntoWrapIter`]: struct.IntoWrapIter.html -/// [`WrapIter`]: struct.WrapIter.html -pub fn wrap_iter(s: &str, width: usize) -> IntoWrapIter<HyphenSplitter> { - Wrapper::new(width).into_wrap_iter(s) -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "hyphenation")] - extern crate hyphenation; - - use super::*; - #[cfg(feature = "hyphenation")] - use hyphenation::{Language, Load, Standard}; - - #[test] - fn no_wrap() { - assert_eq!(wrap("foo", 10), vec!["foo"]); - } - - #[test] - fn simple() { - assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]); - } - - #[test] - fn multi_word_on_line() { - assert_eq!(wrap("foo bar baz", 10), vec!["foo bar", "baz"]); - } - - #[test] - fn long_word() { - assert_eq!(wrap("foo", 0), vec!["f", "o", "o"]); - } - - #[test] - fn long_words() { - assert_eq!(wrap("foo bar", 0), vec!["f", "o", "o", "b", "a", "r"]); - } - - #[test] - fn max_width() { - assert_eq!(wrap("foo bar", usize::max_value()), vec!["foo bar"]); - } - - #[test] - fn leading_whitespace() { - assert_eq!(wrap(" foo bar", 6), vec![" foo", "bar"]); - } - - #[test] - fn trailing_whitespace() { - assert_eq!(wrap("foo bar ", 6), vec!["foo", "bar "]); - } - - #[test] - fn interior_whitespace() { - assert_eq!(wrap("foo: bar baz", 10), vec!["foo: bar", "baz"]); - } - - #[test] - fn extra_whitespace_start_of_line() { - // Whitespace is only significant inside a line. After a line - // gets too long and is broken, the first word starts in - // column zero and is not indented. The line before might end - // up with trailing whitespace. - assert_eq!(wrap("foo bar", 5), vec!["foo", "bar"]); - } - - #[test] - fn issue_99() { - // We did not reset the in_whitespace flag correctly and did - // not handle single-character words after a line break. - assert_eq!( - wrap("aaabbbccc x yyyzzzwww", 9), - vec!["aaabbbccc", "x", "yyyzzzwww"] - ); - } - - #[test] - fn issue_129() { - // The dash is an em-dash which takes up four bytes. We used - // to panic since we tried to index into the character. - assert_eq!(wrap("x – x", 1), vec!["x", "–", "x"]); - } - - #[test] - fn wide_character_handling() { - assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]); - assert_eq!( - wrap("Hello, World!", 15), - vec!["Hello,", "World!"] - ); - } - - #[test] - fn empty_input_not_indented() { - let wrapper = Wrapper::new(10).initial_indent("!!!"); - assert_eq!(wrapper.fill(""), ""); - } - - #[test] - fn indent_single_line() { - let wrapper = Wrapper::new(10).initial_indent(">>>"); // No trailing space - assert_eq!(wrapper.fill("foo"), ">>>foo"); - } - - #[test] - fn indent_multiple_lines() { - let wrapper = Wrapper::new(6).initial_indent("* ").subsequent_indent(" "); - assert_eq!(wrapper.wrap("foo bar baz"), vec!["* foo", " bar", " baz"]); - } - - #[test] - fn indent_break_words() { - let wrapper = Wrapper::new(5).initial_indent("* ").subsequent_indent(" "); - assert_eq!(wrapper.wrap("foobarbaz"), vec!["* foo", " bar", " baz"]); - } - - #[test] - fn hyphens() { - assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]); - } - - #[test] - fn trailing_hyphen() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!(wrapper.wrap("foobar-"), vec!["foobar-"]); - } - - #[test] - fn multiple_hyphens() { - assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]); - } - - #[test] - fn hyphens_flag() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!( - wrapper.wrap("The --foo-bar flag."), - vec!["The", "--foo-", "bar", "flag."] - ); - } - - #[test] - fn repeated_hyphens() { - let wrapper = Wrapper::new(4).break_words(false); - assert_eq!(wrapper.wrap("foo--bar"), vec!["foo--bar"]); - } - - #[test] - fn hyphens_alphanumeric() { - assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]); - } - - #[test] - fn hyphens_non_alphanumeric() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!(wrapper.wrap("foo(-)bar"), vec!["foo(-)bar"]); - } - - #[test] - fn multiple_splits() { - assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]); - } - - #[test] - fn forced_split() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!(wrapper.wrap("foobar-baz"), vec!["foobar-", "baz"]); - } - - #[test] - fn no_hyphenation() { - let wrapper = Wrapper::with_splitter(8, NoHyphenation); - assert_eq!(wrapper.wrap("foo bar-baz"), vec!["foo", "bar-baz"]); - } - - #[test] - #[cfg(feature = "hyphenation")] - fn auto_hyphenation() { - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let wrapper = Wrapper::new(10); - assert_eq!( - wrapper.wrap("Internationalization"), - vec!["Internatio", "nalization"] - ); - - let wrapper = Wrapper::with_splitter(10, dictionary); - assert_eq!( - wrapper.wrap("Internationalization"), - vec!["Interna-", "tionaliza-", "tion"] - ); - } - - #[test] - #[cfg(feature = "hyphenation")] - fn split_len_hyphenation() { - // Test that hyphenation takes the width of the wihtespace - // into account. - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let wrapper = Wrapper::with_splitter(15, dictionary); - assert_eq!( - wrapper.wrap("garbage collection"), - vec!["garbage col-", "lection"] - ); - } - - #[test] - #[cfg(feature = "hyphenation")] - fn borrowed_lines() { - // Lines that end with an extra hyphen are owned, the final - // line is borrowed. - use std::borrow::Cow::{Borrowed, Owned}; - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let wrapper = Wrapper::with_splitter(10, dictionary); - let lines = wrapper.wrap("Internationalization"); - if let Borrowed(s) = lines[0] { - assert!(false, "should not have been borrowed: {:?}", s); - } - if let Borrowed(s) = lines[1] { - assert!(false, "should not have been borrowed: {:?}", s); - } - if let Owned(ref s) = lines[2] { - assert!(false, "should not have been owned: {:?}", s); - } - } - - #[test] - #[cfg(feature = "hyphenation")] - fn auto_hyphenation_with_hyphen() { - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let wrapper = Wrapper::new(8).break_words(false); - assert_eq!(wrapper.wrap("over-caffinated"), vec!["over-", "caffinated"]); - - let wrapper = Wrapper::with_splitter(8, dictionary).break_words(false); - assert_eq!( - wrapper.wrap("over-caffinated"), - vec!["over-", "caffi-", "nated"] - ); - } - - #[test] - fn break_words() { - assert_eq!(wrap("foobarbaz", 3), vec!["foo", "bar", "baz"]); - } - - #[test] - fn break_words_wide_characters() { - assert_eq!(wrap("Hello", 5), vec!["He", "ll", "o"]); - } - - #[test] - fn break_words_zero_width() { - assert_eq!(wrap("foobar", 0), vec!["f", "o", "o", "b", "a", "r"]); - } - - #[test] - fn break_words_line_breaks() { - assert_eq!(fill("ab\ncdefghijkl", 5), "ab\ncdefg\nhijkl"); - assert_eq!(fill("abcdefgh\nijkl", 5), "abcde\nfgh\nijkl"); - } - - #[test] - fn preserve_line_breaks() { - assert_eq!(fill("test\n", 11), "test\n"); - assert_eq!(fill("test\n\na\n\n", 11), "test\n\na\n\n"); - assert_eq!(fill("1 3 5 7\n1 3 5 7", 7), "1 3 5 7\n1 3 5 7"); - } - - #[test] - fn wrap_preserve_line_breaks() { - assert_eq!(fill("1 3 5 7\n1 3 5 7", 5), "1 3 5\n7\n1 3 5\n7"); - } - - #[test] - fn non_breaking_space() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!(wrapper.fill("foo bar baz"), "foo bar baz"); - } - - #[test] - fn non_breaking_hyphen() { - let wrapper = Wrapper::new(5).break_words(false); - assert_eq!(wrapper.fill("foo‑bar‑baz"), "foo‑bar‑baz"); - } - - #[test] - fn fill_simple() { - assert_eq!(fill("foo bar baz", 10), "foo bar\nbaz"); - } -} |