From 1b6a04ca5504955c571d1c97504fb45ea0befee4 Mon Sep 17 00:00:00 2001
From: Valentin Popov <valentin@popov.link>
Date: Mon, 8 Jan 2024 01:21:28 +0400
Subject: Initial vendor packages

Signed-off-by: Valentin Popov <valentin@popov.link>
---
 vendor/textwrap/src/core.rs                        |  433 +++++
 vendor/textwrap/src/indentation.rs                 |  347 ++++
 vendor/textwrap/src/lib.rs                         | 1847 ++++++++++++++++++++
 vendor/textwrap/src/word_separators.rs             |  428 +++++
 vendor/textwrap/src/word_splitters.rs              |  314 ++++
 vendor/textwrap/src/wrap_algorithms.rs             |  381 ++++
 vendor/textwrap/src/wrap_algorithms/optimal_fit.rs |  433 +++++
 7 files changed, 4183 insertions(+)
 create mode 100644 vendor/textwrap/src/core.rs
 create mode 100644 vendor/textwrap/src/indentation.rs
 create mode 100644 vendor/textwrap/src/lib.rs
 create mode 100644 vendor/textwrap/src/word_separators.rs
 create mode 100644 vendor/textwrap/src/word_splitters.rs
 create mode 100644 vendor/textwrap/src/wrap_algorithms.rs
 create mode 100644 vendor/textwrap/src/wrap_algorithms/optimal_fit.rs

(limited to 'vendor/textwrap/src')

diff --git a/vendor/textwrap/src/core.rs b/vendor/textwrap/src/core.rs
new file mode 100644
index 0000000..0ab4ef8
--- /dev/null
+++ b/vendor/textwrap/src/core.rs
@@ -0,0 +1,433 @@
+//! Building blocks for advanced wrapping functionality.
+//!
+//! The functions and structs in this module can be used to implement
+//! advanced wrapping functionality when the [`wrap`](super::wrap) and
+//! [`fill`](super::fill) function don't do what you want.
+//!
+//! In general, you want to follow these steps when wrapping
+//! something:
+//!
+//! 1. Split your input into [`Fragment`]s. These are abstract blocks
+//!    of text or content which can be wrapped into lines. See
+//!    [`WordSeparator`](crate::word_separators::WordSeparator) for
+//!    how to do this for text.
+//!
+//! 2. Potentially split your fragments into smaller pieces. This
+//!    allows you to implement things like hyphenation. If you use the
+//!    `Word` type, you can use [`WordSplitter`](crate::WordSplitter)
+//!    enum for this.
+//!
+//! 3. Potentially break apart fragments that are still too large to
+//!    fit on a single line. This is implemented in [`break_words`].
+//!
+//! 4. Finally take your fragments and put them into lines. There are
+//!    two algorithms for this in the
+//!    [`wrap_algorithms`](crate::wrap_algorithms) module:
+//!    [`wrap_optimal_fit`](crate::wrap_algorithms::wrap_optimal_fit)
+//!    and [`wrap_first_fit`](crate::wrap_algorithms::wrap_first_fit).
+//!    The former produces better line breaks, the latter is faster.
+//!
+//! 5. Iterate through the slices returned by the wrapping functions
+//!    and construct your lines of output.
+//!
+//! Please [open an issue](https://github.com/mgeisler/textwrap/) if
+//! the functionality here is not sufficient or if you have ideas for
+//! improving it. We would love to hear from you!
+
+/// The CSI or “Control Sequence Introducer” introduces an ANSI escape
+/// sequence. This is typically used for colored text and will be
+/// ignored when computing the text width.
+const CSI: (char, char) = ('\x1b', '[');
+/// The final bytes of an ANSI escape sequence must be in this range.
+const ANSI_FINAL_BYTE: std::ops::RangeInclusive<char> = '\x40'..='\x7e';
+
+/// Skip ANSI escape sequences. The `ch` is the current `char`, the
+/// `chars` provide the following characters. The `chars` will be
+/// modified if `ch` is the start of an ANSI escape sequence.
+#[inline]
+pub(crate) fn skip_ansi_escape_sequence<I: Iterator<Item = char>>(ch: char, chars: &mut I) -> bool {
+    if ch == CSI.0 && chars.next() == Some(CSI.1) {
+        // We have found the start of an ANSI escape code, typically
+        // used for colored terminal text. We skip until we find a
+        // "final byte" in the range 0x40–0x7E.
+        for ch in chars {
+            if ANSI_FINAL_BYTE.contains(&ch) {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+#[cfg(feature = "unicode-width")]
+#[inline]
+fn ch_width(ch: char) -> usize {
+    unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0)
+}
+
+/// First character which [`ch_width`] will classify as double-width.
+/// Please see [`display_width`].
+#[cfg(not(feature = "unicode-width"))]
+const DOUBLE_WIDTH_CUTOFF: char = '\u{1100}';
+
+#[cfg(not(feature = "unicode-width"))]
+#[inline]
+fn ch_width(ch: char) -> usize {
+    if ch < DOUBLE_WIDTH_CUTOFF {
+        1
+    } else {
+        2
+    }
+}
+
+/// Compute the display width of `text` while skipping over ANSI
+/// escape sequences.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::core::display_width;
+///
+/// assert_eq!(display_width("Café Plain"), 10);
+/// assert_eq!(display_width("\u{1b}[31mCafé Rouge\u{1b}[0m"), 10);
+/// ```
+///
+/// **Note:** When the `unicode-width` Cargo feature is disabled, the
+/// width of a `char` is determined by a crude approximation which
+/// simply counts chars below U+1100 as 1 column wide, and all other
+/// characters as 2 columns wide. With the feature enabled, function
+/// will correctly deal with [combining characters] in their
+/// decomposed form (see [Unicode equivalence]).
+///
+/// An example of a decomposed character is “é”, which can be
+/// decomposed into: “e” followed by a combining acute accent: “◌́”.
+/// Without the `unicode-width` Cargo feature, every `char` below
+/// U+1100 has a width of 1. This includes the combining accent:
+///
+/// ```
+/// use textwrap::core::display_width;
+///
+/// assert_eq!(display_width("Cafe Plain"), 10);
+/// #[cfg(feature = "unicode-width")]
+/// assert_eq!(display_width("Cafe\u{301} Plain"), 10);
+/// #[cfg(not(feature = "unicode-width"))]
+/// assert_eq!(display_width("Cafe\u{301} Plain"), 11);
+/// ```
+///
+/// ## Emojis and CJK Characters
+///
+/// Characters such as emojis and [CJK characters] used in the
+/// Chinese, Japanese, and Korean langauges are seen as double-width,
+/// even if the `unicode-width` feature is disabled:
+///
+/// ```
+/// use textwrap::core::display_width;
+///
+/// assert_eq!(display_width("😂😭🥺🤣✨😍🙏🥰😊🔥"), 20);
+/// assert_eq!(display_width("你好"), 4);  // “Nǐ hǎo” or “Hello” in Chinese
+/// ```
+///
+/// # Limitations
+///
+/// The displayed width of a string cannot always be computed from the
+/// string alone. This is because the width depends on the rendering
+/// engine used. This is particularly visible with [emoji modifier
+/// sequences] where a base emoji is modified with, e.g., skin tone or
+/// hair color modifiers. It is up to the rendering engine to detect
+/// this and to produce a suitable emoji.
+///
+/// A simple example is “❤️”, which consists of “❤” (U+2764: Black
+/// Heart Symbol) followed by U+FE0F (Variation Selector-16). By
+/// itself, “❤” is a black heart, but if you follow it with the
+/// variant selector, you may get a wider red heart.
+///
+/// A more complex example would be “👨‍🦰” which should depict a man
+/// with red hair. Here the computed width is too large — and the
+/// width differs depending on the use of the `unicode-width` feature:
+///
+/// ```
+/// use textwrap::core::display_width;
+///
+/// assert_eq!("👨‍🦰".chars().collect::<Vec<char>>(), ['\u{1f468}', '\u{200d}', '\u{1f9b0}']);
+/// #[cfg(feature = "unicode-width")]
+/// assert_eq!(display_width("👨‍🦰"), 4);
+/// #[cfg(not(feature = "unicode-width"))]
+/// assert_eq!(display_width("👨‍🦰"), 6);
+/// ```
+///
+/// This happens because the grapheme consists of three code points:
+/// “👨” (U+1F468: Man), Zero Width Joiner (U+200D), and “🦰”
+/// (U+1F9B0: Red Hair). You can see them above in the test. With
+/// `unicode-width` enabled, the ZWJ is correctly seen as having zero
+/// width, without it is counted as a double-width character.
+///
+/// ## Terminal Support
+///
+/// Modern browsers typically do a great job at combining characters
+/// as shown above, but terminals often struggle more. As an example,
+/// Gnome Terminal version 3.38.1, shows “❤️” as a big red heart, but
+/// shows "👨‍🦰" as “👨🦰”.
+///
+/// [combining characters]: https://en.wikipedia.org/wiki/Combining_character
+/// [Unicode equivalence]: https://en.wikipedia.org/wiki/Unicode_equivalence
+/// [CJK characters]: https://en.wikipedia.org/wiki/CJK_characters
+/// [emoji modifier sequences]: https://unicode.org/emoji/charts/full-emoji-modifiers.html
+pub fn display_width(text: &str) -> usize {
+    let mut chars = text.chars();
+    let mut width = 0;
+    while let Some(ch) = chars.next() {
+        if skip_ansi_escape_sequence(ch, &mut chars) {
+            continue;
+        }
+        width += ch_width(ch);
+    }
+    width
+}
+
+/// A (text) fragment denotes the unit which we wrap into lines.
+///
+/// Fragments represent an abstract _word_ plus the _whitespace_
+/// following the word. In case the word falls at the end of the line,
+/// the whitespace is dropped and a so-called _penalty_ is inserted
+/// instead (typically `"-"` if the word was hyphenated).
+///
+/// For wrapping purposes, the precise content of the word, the
+/// whitespace, and the penalty is irrelevant. All we need to know is
+/// the displayed width of each part, which this trait provides.
+pub trait Fragment: std::fmt::Debug {
+    /// Displayed width of word represented by this fragment.
+    fn width(&self) -> f64;
+
+    /// Displayed width of the whitespace that must follow the word
+    /// when the word is not at the end of a line.
+    fn whitespace_width(&self) -> f64;
+
+    /// Displayed width of the penalty that must be inserted if the
+    /// word falls at the end of a line.
+    fn penalty_width(&self) -> f64;
+}
+
+/// A piece of wrappable text, including any trailing whitespace.
+///
+/// A `Word` is an example of a [`Fragment`], so it has a width,
+/// trailing whitespace, and potentially a penalty item.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct Word<'a> {
+    /// Word content.
+    pub word: &'a str,
+    /// Whitespace to insert if the word does not fall at the end of a line.
+    pub whitespace: &'a str,
+    /// Penalty string to insert if the word falls at the end of a line.
+    pub penalty: &'a str,
+    // Cached width in columns.
+    pub(crate) width: usize,
+}
+
+impl std::ops::Deref for Word<'_> {
+    type Target = str;
+
+    fn deref(&self) -> &Self::Target {
+        self.word
+    }
+}
+
+impl<'a> Word<'a> {
+    /// Construct a `Word` from a string.
+    ///
+    /// A trailing stretch of `' '` is automatically taken to be the
+    /// whitespace part of the word.
+    pub fn from(word: &str) -> Word<'_> {
+        let trimmed = word.trim_end_matches(' ');
+        Word {
+            word: trimmed,
+            width: display_width(trimmed),
+            whitespace: &word[trimmed.len()..],
+            penalty: "",
+        }
+    }
+
+    /// Break this word into smaller words with a width of at most
+    /// `line_width`. The whitespace and penalty from this `Word` is
+    /// added to the last piece.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::core::Word;
+    /// assert_eq!(
+    ///     Word::from("Hello!  ").break_apart(3).collect::<Vec<_>>(),
+    ///     vec![Word::from("Hel"), Word::from("lo!  ")]
+    /// );
+    /// ```
+    pub fn break_apart<'b>(&'b self, line_width: usize) -> impl Iterator<Item = Word<'a>> + 'b {
+        let mut char_indices = self.word.char_indices();
+        let mut offset = 0;
+        let mut width = 0;
+
+        std::iter::from_fn(move || {
+            while let Some((idx, ch)) = char_indices.next() {
+                if skip_ansi_escape_sequence(ch, &mut char_indices.by_ref().map(|(_, ch)| ch)) {
+                    continue;
+                }
+
+                if width > 0 && width + ch_width(ch) > line_width {
+                    let word = Word {
+                        word: &self.word[offset..idx],
+                        width: width,
+                        whitespace: "",
+                        penalty: "",
+                    };
+                    offset = idx;
+                    width = ch_width(ch);
+                    return Some(word);
+                }
+
+                width += ch_width(ch);
+            }
+
+            if offset < self.word.len() {
+                let word = Word {
+                    word: &self.word[offset..],
+                    width: width,
+                    whitespace: self.whitespace,
+                    penalty: self.penalty,
+                };
+                offset = self.word.len();
+                return Some(word);
+            }
+
+            None
+        })
+    }
+}
+
+impl Fragment for Word<'_> {
+    #[inline]
+    fn width(&self) -> f64 {
+        self.width as f64
+    }
+
+    // We assume the whitespace consist of ' ' only. This allows us to
+    // compute the display width in constant time.
+    #[inline]
+    fn whitespace_width(&self) -> f64 {
+        self.whitespace.len() as f64
+    }
+
+    // We assume the penalty is `""` or `"-"`. This allows us to
+    // compute the display width in constant time.
+    #[inline]
+    fn penalty_width(&self) -> f64 {
+        self.penalty.len() as f64
+    }
+}
+
+/// Forcibly break words wider than `line_width` into smaller words.
+///
+/// This simply calls [`Word::break_apart`] on words that are too
+/// wide. This means that no extra `'-'` is inserted, the word is
+/// simply broken into smaller pieces.
+pub fn break_words<'a, I>(words: I, line_width: usize) -> Vec<Word<'a>>
+where
+    I: IntoIterator<Item = Word<'a>>,
+{
+    let mut shortened_words = Vec::new();
+    for word in words {
+        if word.width() > line_width as f64 {
+            shortened_words.extend(word.break_apart(line_width));
+        } else {
+            shortened_words.push(word);
+        }
+    }
+    shortened_words
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[cfg(feature = "unicode-width")]
+    use unicode_width::UnicodeWidthChar;
+
+    #[test]
+    fn skip_ansi_escape_sequence_works() {
+        let blue_text = "\u{1b}[34mHello\u{1b}[0m";
+        let mut chars = blue_text.chars();
+        let ch = chars.next().unwrap();
+        assert!(skip_ansi_escape_sequence(ch, &mut chars));
+        assert_eq!(chars.next(), Some('H'));
+    }
+
+    #[test]
+    fn emojis_have_correct_width() {
+        use unic_emoji_char::is_emoji;
+
+        // Emojis in the Basic Latin (ASCII) and Latin-1 Supplement
+        // blocks all have a width of 1 column. This includes
+        // characters such as '#' and '©'.
+        for ch in '\u{1}'..'\u{FF}' {
+            if is_emoji(ch) {
+                let desc = format!("{:?} U+{:04X}", ch, ch as u32);
+
+                #[cfg(feature = "unicode-width")]
+                assert_eq!(ch.width().unwrap(), 1, "char: {}", desc);
+
+                #[cfg(not(feature = "unicode-width"))]
+                assert_eq!(ch_width(ch), 1, "char: {}", desc);
+            }
+        }
+
+        // Emojis in the remaining blocks of the Basic Multilingual
+        // Plane (BMP), in the Supplementary Multilingual Plane (SMP),
+        // and in the Supplementary Ideographic Plane (SIP), are all 1
+        // or 2 columns wide when unicode-width is used, and always 2
+        // columns wide otherwise. This includes all of our favorite
+        // emojis such as 😊.
+        for ch in '\u{FF}'..'\u{2FFFF}' {
+            if is_emoji(ch) {
+                let desc = format!("{:?} U+{:04X}", ch, ch as u32);
+
+                #[cfg(feature = "unicode-width")]
+                assert!(ch.width().unwrap() <= 2, "char: {}", desc);
+
+                #[cfg(not(feature = "unicode-width"))]
+                assert_eq!(ch_width(ch), 2, "char: {}", desc);
+            }
+        }
+
+        // The remaining planes contain almost no assigned code points
+        // and thus also no emojis.
+    }
+
+    #[test]
+    fn display_width_works() {
+        assert_eq!("Café Plain".len(), 11); // “é” is two bytes
+        assert_eq!(display_width("Café Plain"), 10);
+        assert_eq!(display_width("\u{1b}[31mCafé Rouge\u{1b}[0m"), 10);
+    }
+
+    #[test]
+    fn display_width_narrow_emojis() {
+        #[cfg(feature = "unicode-width")]
+        assert_eq!(display_width("⁉"), 1);
+
+        // The ⁉ character is above DOUBLE_WIDTH_CUTOFF.
+        #[cfg(not(feature = "unicode-width"))]
+        assert_eq!(display_width("⁉"), 2);
+    }
+
+    #[test]
+    fn display_width_narrow_emojis_variant_selector() {
+        #[cfg(feature = "unicode-width")]
+        assert_eq!(display_width("⁉\u{fe0f}"), 1);
+
+        // The variant selector-16 is also counted.
+        #[cfg(not(feature = "unicode-width"))]
+        assert_eq!(display_width("⁉\u{fe0f}"), 4);
+    }
+
+    #[test]
+    fn display_width_emojis() {
+        assert_eq!(display_width("😂😭🥺🤣✨😍🙏🥰😊🔥"), 20);
+    }
+}
diff --git a/vendor/textwrap/src/indentation.rs b/vendor/textwrap/src/indentation.rs
new file mode 100644
index 0000000..5d90c06
--- /dev/null
+++ b/vendor/textwrap/src/indentation.rs
@@ -0,0 +1,347 @@
+//! Functions related to adding and removing indentation from lines of
+//! text.
+//!
+//! The functions here can be used to uniformly indent or dedent
+//! (unindent) word wrapped lines of text.
+
+/// Indent each line by the given prefix.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::indent;
+///
+/// assert_eq!(indent("First line.\nSecond line.\n", "  "),
+///            "  First line.\n  Second line.\n");
+/// ```
+///
+/// When indenting, trailing whitespace is stripped from the prefix.
+/// This means that empty lines remain empty afterwards:
+///
+/// ```
+/// use textwrap::indent;
+///
+/// assert_eq!(indent("First line.\n\n\nSecond line.\n", "  "),
+///            "  First line.\n\n\n  Second line.\n");
+/// ```
+///
+/// Notice how `"\n\n\n"` remained as `"\n\n\n"`.
+///
+/// This feature is useful when you want to indent text and have a
+/// space between your prefix and the text. In this case, you _don't_
+/// want a trailing space on empty lines:
+///
+/// ```
+/// use textwrap::indent;
+///
+/// assert_eq!(indent("foo = 123\n\nprint(foo)\n", "# "),
+///            "# foo = 123\n#\n# print(foo)\n");
+/// ```
+///
+/// Notice how `"\n\n"` became `"\n#\n"` instead of `"\n# \n"` which
+/// would have trailing whitespace.
+///
+/// Leading and trailing whitespace coming from the text itself is
+/// kept unchanged:
+///
+/// ```
+/// use textwrap::indent;
+///
+/// assert_eq!(indent(" \t  Foo   ", "->"), "-> \t  Foo   ");
+/// ```
+pub fn indent(s: &str, prefix: &str) -> String {
+    // We know we'll need more than s.len() bytes for the output, but
+    // without counting '\n' characters (which is somewhat slow), we
+    // don't know exactly how much. However, we can preemptively do
+    // the first doubling of the output size.
+    let mut result = String::with_capacity(2 * s.len());
+    let trimmed_prefix = prefix.trim_end();
+    for (idx, line) in s.split_terminator('\n').enumerate() {
+        if idx > 0 {
+            result.push('\n');
+        }
+        if line.trim().is_empty() {
+            result.push_str(trimmed_prefix);
+        } else {
+            result.push_str(prefix);
+        }
+        result.push_str(line);
+    }
+    if s.ends_with('\n') {
+        // split_terminator will have eaten the final '\n'.
+        result.push('\n');
+    }
+    result
+}
+
+/// Removes common leading whitespace from each line.
+///
+/// This function will look at each non-empty line and determine the
+/// maximum amount of whitespace that can be removed from all lines:
+///
+/// ```
+/// use textwrap::dedent;
+///
+/// assert_eq!(dedent("
+///     1st line
+///       2nd line
+///     3rd line
+/// "), "
+/// 1st line
+///   2nd line
+/// 3rd line
+/// ");
+/// ```
+pub fn dedent(s: &str) -> String {
+    let mut prefix = "";
+    let mut lines = s.lines();
+
+    // We first search for a non-empty line to find a prefix.
+    for line in &mut lines {
+        let mut whitespace_idx = line.len();
+        for (idx, ch) in line.char_indices() {
+            if !ch.is_whitespace() {
+                whitespace_idx = idx;
+                break;
+            }
+        }
+
+        // Check if the line had anything but whitespace
+        if whitespace_idx < line.len() {
+            prefix = &line[..whitespace_idx];
+            break;
+        }
+    }
+
+    // We then continue looking through the remaining lines to
+    // possibly shorten the prefix.
+    for line in &mut lines {
+        let mut whitespace_idx = line.len();
+        for ((idx, a), b) in line.char_indices().zip(prefix.chars()) {
+            if a != b {
+                whitespace_idx = idx;
+                break;
+            }
+        }
+
+        // Check if the line had anything but whitespace and if we
+        // have found a shorter prefix
+        if whitespace_idx < line.len() && whitespace_idx < prefix.len() {
+            prefix = &line[..whitespace_idx];
+        }
+    }
+
+    // We now go over the lines a second time to build the result.
+    let mut result = String::new();
+    for line in s.lines() {
+        if line.starts_with(&prefix) && line.chars().any(|c| !c.is_whitespace()) {
+            let (_, tail) = line.split_at(prefix.len());
+            result.push_str(tail);
+        }
+        result.push('\n');
+    }
+
+    if result.ends_with('\n') && !s.ends_with('\n') {
+        let new_len = result.len() - 1;
+        result.truncate(new_len);
+    }
+
+    result
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn indent_empty() {
+        assert_eq!(indent("\n", "  "), "\n");
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn indent_nonempty() {
+        let text = [
+            "  foo\n",
+            "bar\n",
+            "  baz\n",
+        ].join("");
+        let expected = [
+            "//   foo\n",
+            "// bar\n",
+            "//   baz\n",
+        ].join("");
+        assert_eq!(indent(&text, "// "), expected);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn indent_empty_line() {
+        let text = [
+            "  foo",
+            "bar",
+            "",
+            "  baz",
+        ].join("\n");
+        let expected = [
+            "//   foo",
+            "// bar",
+            "//",
+            "//   baz",
+        ].join("\n");
+        assert_eq!(indent(&text, "// "), expected);
+    }
+
+    #[test]
+    fn dedent_empty() {
+        assert_eq!(dedent(""), "");
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_multi_line() {
+        let x = [
+            "    foo",
+            "  bar",
+            "    baz",
+        ].join("\n");
+        let y = [
+            "  foo",
+            "bar",
+            "  baz"
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_empty_line() {
+        let x = [
+            "    foo",
+            "  bar",
+            "   ",
+            "    baz"
+        ].join("\n");
+        let y = [
+            "  foo",
+            "bar",
+            "",
+            "  baz"
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_blank_line() {
+        let x = [
+            "      foo",
+            "",
+            "        bar",
+            "          foo",
+            "          bar",
+            "          baz",
+        ].join("\n");
+        let y = [
+            "foo",
+            "",
+            "  bar",
+            "    foo",
+            "    bar",
+            "    baz",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_whitespace_line() {
+        let x = [
+            "      foo",
+            " ",
+            "        bar",
+            "          foo",
+            "          bar",
+            "          baz",
+        ].join("\n");
+        let y = [
+            "foo",
+            "",
+            "  bar",
+            "    foo",
+            "    bar",
+            "    baz",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_mixed_whitespace() {
+        let x = [
+            "\tfoo",
+            "  bar",
+        ].join("\n");
+        let y = [
+            "\tfoo",
+            "  bar",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_tabbed_whitespace() {
+        let x = [
+            "\t\tfoo",
+            "\t\t\tbar",
+        ].join("\n");
+        let y = [
+            "foo",
+            "\tbar",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_mixed_tabbed_whitespace() {
+        let x = [
+            "\t  \tfoo",
+            "\t  \t\tbar",
+        ].join("\n");
+        let y = [
+            "foo",
+            "\tbar",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_mixed_tabbed_whitespace2() {
+        let x = [
+            "\t  \tfoo",
+            "\t    \tbar",
+        ].join("\n");
+        let y = [
+            "\tfoo",
+            "  \tbar",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn dedent_preserve_no_terminating_newline() {
+        let x = [
+            "  foo",
+            "    bar",
+        ].join("\n");
+        let y = [
+            "foo",
+            "  bar",
+        ].join("\n");
+        assert_eq!(dedent(&x), y);
+    }
+}
diff --git a/vendor/textwrap/src/lib.rs b/vendor/textwrap/src/lib.rs
new file mode 100644
index 0000000..e570eac
--- /dev/null
+++ b/vendor/textwrap/src/lib.rs
@@ -0,0 +1,1847 @@
+//! The textwrap library provides functions for word wrapping and
+//! indenting text.
+//!
+//! # Wrapping Text
+//!
+//! Wrapping text can be very useful in command-line programs where
+//! you want to format dynamic output nicely so it looks good in a
+//! terminal. A quick example:
+//!
+//! ```
+//! # #[cfg(feature = "smawk")] {
+//! let text = "textwrap: a small library for wrapping text.";
+//! assert_eq!(textwrap::wrap(text, 18),
+//!            vec!["textwrap: a",
+//!                 "small library for",
+//!                 "wrapping text."]);
+//! # }
+//! ```
+//!
+//! The [`wrap`] function returns the individual lines, use [`fill`]
+//! is you want the lines joined with `'\n'` to form a `String`.
+//!
+//! If you enable the `hyphenation` Cargo feature, you can get
+//! automatic hyphenation for a number of languages:
+//!
+//! ```
+//! #[cfg(feature = "hyphenation")] {
+//! use hyphenation::{Language, Load, Standard};
+//! use textwrap::{wrap, Options, WordSplitter};
+//!
+//! let text = "textwrap: a small library for wrapping text.";
+//! let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
+//! let options = Options::new(18).word_splitter(WordSplitter::Hyphenation(dictionary));
+//! assert_eq!(wrap(text, &options),
+//!            vec!["textwrap: a small",
+//!                 "library for wrap-",
+//!                 "ping text."]);
+//! }
+//! ```
+//!
+//! See also the [`unfill`] and [`refill`] functions which allow you to
+//! manipulate already wrapped text.
+//!
+//! ## Wrapping Strings at Compile Time
+//!
+//! If your strings are known at compile time, please take a look at
+//! the procedural macros from the [textwrap-macros] crate.
+//!
+//! ## 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 will by default measure the
+//! width of text using the _displayed width_, not the size in bytes.
+//! The `unicode-width` Cargo feature controls this.
+//!
+//! 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 when the
+//! `unicode-width` Cargo feature is enabled (it is enabled by
+//! default).
+//!
+//! # Indentation and Dedentation
+//!
+//! The textwrap library also offers functions for adding a prefix to
+//! every line of a string and to remove leading whitespace. As an
+//! example, the [`indent`] function allows you to turn lines of text
+//! into a bullet list:
+//!
+//! ```
+//! let before = "\
+//! foo
+//! bar
+//! baz
+//! ";
+//! let after = "\
+//! * foo
+//! * bar
+//! * baz
+//! ";
+//! assert_eq!(textwrap::indent(before, "* "), after);
+//! ```
+//!
+//! Removing leading whitespace is done with [`dedent`]:
+//!
+//! ```
+//! let before = "
+//!     Some
+//!       indented
+//!         text
+//! ";
+//! let after = "
+//! Some
+//!   indented
+//!     text
+//! ";
+//! assert_eq!(textwrap::dedent(before), after);
+//! ```
+//!
+//! # Cargo Features
+//!
+//! The textwrap library can be slimmed down as needed via a number of
+//! Cargo features. This means you only pay for the features you
+//! actually use.
+//!
+//! The full dependency graph, where dashed lines indicate optional
+//! dependencies, is shown below:
+//!
+//! <img src="https://raw.githubusercontent.com/mgeisler/textwrap/master/images/textwrap-0.15.2.svg">
+//!
+//! ## Default Features
+//!
+//! These features are enabled by default:
+//!
+//! * `unicode-linebreak`: enables finding words using the
+//!   [unicode-linebreak] crate, which implements the line breaking
+//!   algorithm described in [Unicode Standard Annex
+//!   #14](https://www.unicode.org/reports/tr14/).
+//!
+//!   This feature can be disabled if you are happy to find words
+//!   separated by ASCII space characters only. People wrapping text
+//!   with emojis or East-Asian characters will want most likely want
+//!   to enable this feature. See [`WordSeparator`] for details.
+//!
+//! * `unicode-width`: enables correct width computation of non-ASCII
+//!   characters via the [unicode-width] crate. Without this feature,
+//!   every [`char`] is 1 column wide, except for emojis which are 2
+//!   columns wide. See the [`core::display_width`] function for
+//!   details.
+//!
+//!   This feature can be disabled if you only need to wrap ASCII
+//!   text, or if the functions in [`core`] are used directly with
+//!   [`core::Fragment`]s for which the widths have been computed in
+//!   other ways.
+//!
+//! * `smawk`: enables linear-time wrapping of the whole paragraph via
+//!   the [smawk] crate. See the [`wrap_algorithms::wrap_optimal_fit`]
+//!   function for details on the optimal-fit algorithm.
+//!
+//!   This feature can be disabled if you only ever intend to use
+//!   [`wrap_algorithms::wrap_first_fit`].
+//!
+//! With Rust 1.59.0, the size impact of the above features on your
+//! binary is as follows:
+//!
+//! | Configuration                            |  Binary Size |    Delta |
+//! | :---                                     |         ---: |     ---: |
+//! | quick-and-dirty implementation           |       289 KB |     — KB |
+//! | textwrap without default features        |       301 KB |    12 KB |
+//! | textwrap with smawk                      |       317 KB |    28 KB |
+//! | textwrap with unicode-width              |       313 KB |    24 KB |
+//! | textwrap with unicode-linebreak          |       395 KB |   106 KB |
+//!
+//! The above sizes are the stripped sizes and the binary is compiled
+//! in release mode with this profile:
+//!
+//! ```toml
+//! [profile.release]
+//! lto = true
+//! codegen-units = 1
+//! ```
+//!
+//! See the [binary-sizes demo] if you want to reproduce these
+//! results.
+//!
+//! ## Optional Features
+//!
+//! These Cargo features enable new functionality:
+//!
+//! * `terminal_size`: enables automatic detection of the terminal
+//!   width via the [terminal_size] crate. See the
+//!   [`Options::with_termwidth`] constructor for details.
+//!
+//! * `hyphenation`: enables language-sensitive hyphenation via the
+//!   [hyphenation] crate. See the [`word_splitters::WordSplitter`]
+//!   trait for details.
+//!
+//! [unicode-linebreak]: https://docs.rs/unicode-linebreak/
+//! [unicode-width]: https://docs.rs/unicode-width/
+//! [smawk]: https://docs.rs/smawk/
+//! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/master/examples/binary-sizes
+//! [textwrap-macros]: https://docs.rs/textwrap-macros/
+//! [terminal_size]: https://docs.rs/terminal_size/
+//! [hyphenation]: https://docs.rs/hyphenation/
+
+#![doc(html_root_url = "https://docs.rs/textwrap/0.15.2")]
+#![forbid(unsafe_code)] // See https://github.com/mgeisler/textwrap/issues/210
+#![deny(missing_docs)]
+#![deny(missing_debug_implementations)]
+#![allow(clippy::redundant_field_names)]
+
+// Make `cargo test` execute the README doctests.
+#[cfg(doctest)]
+#[doc = include_str!("../README.md")]
+mod readme_doctest {}
+
+use std::borrow::Cow;
+
+mod indentation;
+pub use crate::indentation::{dedent, indent};
+
+mod word_separators;
+pub use word_separators::WordSeparator;
+
+pub mod word_splitters;
+pub use word_splitters::WordSplitter;
+
+pub mod wrap_algorithms;
+pub use wrap_algorithms::WrapAlgorithm;
+
+pub mod core;
+
+#[cfg(feature = "unicode-linebreak")]
+macro_rules! DefaultWordSeparator {
+    () => {
+        WordSeparator::UnicodeBreakProperties
+    };
+}
+
+#[cfg(not(feature = "unicode-linebreak"))]
+macro_rules! DefaultWordSeparator {
+    () => {
+        WordSeparator::AsciiSpace
+    };
+}
+
+/// Holds configuration options for wrapping and filling text.
+#[derive(Debug, Clone)]
+pub struct Options<'a> {
+    /// The width in columns at which the text will be wrapped.
+    pub width: usize,
+    /// Indentation used for the first line of output. See the
+    /// [`Options::initial_indent`] method.
+    pub initial_indent: &'a str,
+    /// Indentation used for subsequent lines of output. See the
+    /// [`Options::subsequent_indent`] method.
+    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`. See the [`Options::break_words`] method.
+    pub break_words: bool,
+    /// Wrapping algorithm to use, see the implementations of the
+    /// [`wrap_algorithms::WrapAlgorithm`] trait for details.
+    pub wrap_algorithm: WrapAlgorithm,
+    /// The line breaking algorithm to use, see
+    /// [`word_separators::WordSeparator`] trait for an overview and
+    /// possible implementations.
+    pub word_separator: WordSeparator,
+    /// The method for splitting words. This can be used to prohibit
+    /// splitting words on hyphens, or it can be used to implement
+    /// language-aware machine hyphenation.
+    pub word_splitter: WordSplitter,
+}
+
+impl<'a> From<&'a Options<'a>> for Options<'a> {
+    fn from(options: &'a Options<'a>) -> Self {
+        Self {
+            width: options.width,
+            initial_indent: options.initial_indent,
+            subsequent_indent: options.subsequent_indent,
+            break_words: options.break_words,
+            word_separator: options.word_separator,
+            wrap_algorithm: options.wrap_algorithm,
+            word_splitter: options.word_splitter.clone(),
+        }
+    }
+}
+
+impl<'a> From<usize> for Options<'a> {
+    fn from(width: usize) -> Self {
+        Options::new(width)
+    }
+}
+
+impl<'a> Options<'a> {
+    /// Creates a new [`Options`] with the specified width. Equivalent to
+    ///
+    /// ```
+    /// # use textwrap::{Options, WordSplitter, WordSeparator, WrapAlgorithm};
+    /// # let width = 80;
+    /// # let actual = Options::new(width);
+    /// # let expected =
+    /// Options {
+    ///     width: width,
+    ///     initial_indent: "",
+    ///     subsequent_indent: "",
+    ///     break_words: true,
+    ///     #[cfg(feature = "unicode-linebreak")]
+    ///     word_separator: WordSeparator::UnicodeBreakProperties,
+    ///     #[cfg(not(feature = "unicode-linebreak"))]
+    ///     word_separator: WordSeparator::AsciiSpace,
+    ///     #[cfg(feature = "smawk")]
+    ///     wrap_algorithm: WrapAlgorithm::new_optimal_fit(),
+    ///     #[cfg(not(feature = "smawk"))]
+    ///     wrap_algorithm: WrapAlgorithm::FirstFit,
+    ///     word_splitter: WordSplitter::HyphenSplitter,
+    /// }
+    /// # ;
+    /// # assert_eq!(actual.width, expected.width);
+    /// # assert_eq!(actual.initial_indent, expected.initial_indent);
+    /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent);
+    /// # assert_eq!(actual.break_words, expected.break_words);
+    /// # assert_eq!(actual.word_splitter, expected.word_splitter);
+    /// ```
+    ///
+    /// Note that the default word separator and wrap algorithms
+    /// changes based on the available Cargo features. The best
+    /// available algorithms are used by default.
+    pub const fn new(width: usize) -> Self {
+        Options {
+            width,
+            initial_indent: "",
+            subsequent_indent: "",
+            break_words: true,
+            word_separator: DefaultWordSeparator!(),
+            wrap_algorithm: WrapAlgorithm::new(),
+            word_splitter: WordSplitter::HyphenSplitter,
+        }
+    }
+
+    /// Creates a new [`Options`] with `width` set to 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
+    /// [`Options::new`].
+    ///
+    /// Equivalent to:
+    ///
+    /// ```no_run
+    /// use textwrap::{termwidth, Options};
+    ///
+    /// let options = Options::new(termwidth());
+    /// ```
+    ///
+    /// **Note:** Only available when the `terminal_size` feature is
+    /// enabled.
+    #[cfg(feature = "terminal_size")]
+    pub fn with_termwidth() -> Self {
+        Self::new(termwidth())
+    }
+}
+
+impl<'a> Options<'a> {
+    /// 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:
+    ///
+    /// ```
+    /// use textwrap::{wrap, Options};
+    ///
+    /// let options = Options::new(16).initial_indent("    ");
+    /// assert_eq!(wrap("This is a little example.", options),
+    ///            vec!["    This is a",
+    ///                 "little example."]);
+    /// ```
+    ///
+    /// [`self.initial_indent`]: #structfield.initial_indent
+    pub fn initial_indent(self, indent: &'a str) -> Self {
+        Options {
+            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:
+    ///
+    /// ```
+    /// use textwrap::{wrap, Options};
+    ///
+    /// let options = Options::new(12)
+    ///     .initial_indent("* ")
+    ///     .subsequent_indent("  ");
+    /// #[cfg(feature = "smawk")]
+    /// assert_eq!(wrap("This is a little example.", options),
+    ///            vec!["* This is",
+    ///                 "  a little",
+    ///                 "  example."]);
+    ///
+    /// // Without the `smawk` feature, the wrapping is a little different:
+    /// #[cfg(not(feature = "smawk"))]
+    /// assert_eq!(wrap("This is a little example.", options),
+    ///            vec!["* This is a",
+    ///                 "  little",
+    ///                 "  example."]);
+    /// ```
+    ///
+    /// [`self.subsequent_indent`]: #structfield.subsequent_indent
+    pub fn subsequent_indent(self, indent: &'a str) -> Self {
+        Options {
+            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.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::{wrap, Options};
+    ///
+    /// let options = Options::new(4).break_words(true);
+    /// assert_eq!(wrap("This is a little example.", options),
+    ///            vec!["This",
+    ///                 "is a",
+    ///                 "litt",
+    ///                 "le",
+    ///                 "exam",
+    ///                 "ple."]);
+    /// ```
+    ///
+    /// [`self.break_words`]: #structfield.break_words
+    pub fn break_words(self, setting: bool) -> Self {
+        Options {
+            break_words: setting,
+            ..self
+        }
+    }
+
+    /// Change [`self.word_separator`].
+    ///
+    /// See [`word_separators::WordSeparator`] for details on the choices.
+    ///
+    /// [`self.word_separator`]: #structfield.word_separator
+    pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> {
+        Options {
+            width: self.width,
+            initial_indent: self.initial_indent,
+            subsequent_indent: self.subsequent_indent,
+            break_words: self.break_words,
+            word_separator: word_separator,
+            wrap_algorithm: self.wrap_algorithm,
+            word_splitter: self.word_splitter,
+        }
+    }
+
+    /// Change [`self.wrap_algorithm`].
+    ///
+    /// See the [`wrap_algorithms::WrapAlgorithm`] trait for details on
+    /// the choices.
+    ///
+    /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm
+    pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> {
+        Options {
+            width: self.width,
+            initial_indent: self.initial_indent,
+            subsequent_indent: self.subsequent_indent,
+            break_words: self.break_words,
+            word_separator: self.word_separator,
+            wrap_algorithm: wrap_algorithm,
+            word_splitter: self.word_splitter,
+        }
+    }
+
+    /// Change [`self.word_splitter`]. The
+    /// [`word_splitters::WordSplitter`] is used to fit part of a word
+    /// into the current line when wrapping text.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::{Options, WordSplitter};
+    /// let opt = Options::new(80);
+    /// assert_eq!(opt.word_splitter, WordSplitter::HyphenSplitter);
+    /// let opt = opt.word_splitter(WordSplitter::NoHyphenation);
+    /// assert_eq!(opt.word_splitter, WordSplitter::NoHyphenation);
+    /// ```
+    ///
+    /// [`self.word_splitter`]: #structfield.word_splitter
+    pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> {
+        Options {
+            width: self.width,
+            initial_indent: self.initial_indent,
+            subsequent_indent: self.subsequent_indent,
+            break_words: self.break_words,
+            word_separator: self.word_separator,
+            wrap_algorithm: self.wrap_algorithm,
+            word_splitter,
+        }
+    }
+}
+
+/// 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 an [`Options`] for wrapping at the current terminal width
+/// with a two column margin to the left and the right:
+///
+/// ```no_run
+/// use textwrap::{termwidth, Options};
+///
+/// let width = termwidth() - 4; // Two columns on each side.
+/// let options = Options::new(width)
+///     .initial_indent("  ")
+///     .subsequent_indent("  ");
+/// ```
+///
+/// **Note:** Only available when the `terminal_size` Cargo feature is
+/// enabled.
+#[cfg(feature = "terminal_size")]
+pub fn termwidth() -> usize {
+    terminal_size::terminal_size().map_or(80, |(terminal_size::Width(w), _)| w.into())
+}
+
+/// Fill a line of text at a given width.
+///
+/// The result is a [`String`], complete with newlines between each
+/// line. Use the [`wrap`] function if you need access to the
+/// individual lines.
+///
+/// The easiest way to use this function is to pass an integer for
+/// `width_or_options`:
+///
+/// ```
+/// use textwrap::fill;
+///
+/// assert_eq!(
+///     fill("Memory safety without garbage collection.", 15),
+///     "Memory safety\nwithout garbage\ncollection."
+/// );
+/// ```
+///
+/// If you need to customize the wrapping, you can pass an [`Options`]
+/// instead of an `usize`:
+///
+/// ```
+/// use textwrap::{fill, Options};
+///
+/// let options = Options::new(15)
+///     .initial_indent("- ")
+///     .subsequent_indent("  ");
+/// assert_eq!(
+///     fill("Memory safety without garbage collection.", &options),
+///     "- Memory safety\n  without\n  garbage\n  collection."
+/// );
+/// ```
+pub fn fill<'a, Opt>(text: &str, width_or_options: Opt) -> String
+where
+    Opt: Into<Options<'a>>,
+{
+    // This will avoid reallocation in simple cases (no
+    // indentation, no hyphenation).
+    let mut result = String::with_capacity(text.len());
+
+    for (i, line) in wrap(text, width_or_options).iter().enumerate() {
+        if i > 0 {
+            result.push('\n');
+        }
+        result.push_str(line);
+    }
+
+    result
+}
+
+/// Unpack a paragraph of already-wrapped text.
+///
+/// This function attempts to recover the original text from a single
+/// paragraph of text produced by the [`fill`] function. This means
+/// that it turns
+///
+/// ```text
+/// textwrap: a small
+/// library for
+/// wrapping text.
+/// ```
+///
+/// back into
+///
+/// ```text
+/// textwrap: a small library for wrapping text.
+/// ```
+///
+/// In addition, it will recognize a common prefix among the lines.
+/// The prefix of the first line is returned in
+/// [`Options::initial_indent`] and the prefix (if any) of the the
+/// other lines is returned in [`Options::subsequent_indent`].
+///
+/// In addition to `' '`, the prefixes can consist of characters used
+/// for unordered lists (`'-'`, `'+'`, and `'*'`) and block quotes
+/// (`'>'`) in Markdown as well as characters often used for inline
+/// comments (`'#'` and `'/'`).
+///
+/// The text must come from a single wrapped paragraph. This means
+/// that there can be no `"\n\n"` within the text.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::unfill;
+///
+/// let (text, options) = unfill("\
+/// * This is an
+///   example of
+///   a list item.
+/// ");
+///
+/// assert_eq!(text, "This is an example of a list item.\n");
+/// assert_eq!(options.initial_indent, "* ");
+/// assert_eq!(options.subsequent_indent, "  ");
+/// ```
+pub fn unfill(text: &str) -> (String, Options<'_>) {
+    let trimmed = text.trim_end_matches('\n');
+    let prefix_chars: &[_] = &[' ', '-', '+', '*', '>', '#', '/'];
+
+    let mut options = Options::new(0);
+    for (idx, line) in trimmed.split('\n').enumerate() {
+        options.width = std::cmp::max(options.width, core::display_width(line));
+        let without_prefix = line.trim_start_matches(prefix_chars);
+        let prefix = &line[..line.len() - without_prefix.len()];
+
+        if idx == 0 {
+            options.initial_indent = prefix;
+        } else if idx == 1 {
+            options.subsequent_indent = prefix;
+        } else if idx > 1 {
+            for ((idx, x), y) in prefix.char_indices().zip(options.subsequent_indent.chars()) {
+                if x != y {
+                    options.subsequent_indent = &prefix[..idx];
+                    break;
+                }
+            }
+            if prefix.len() < options.subsequent_indent.len() {
+                options.subsequent_indent = prefix;
+            }
+        }
+    }
+
+    let mut unfilled = String::with_capacity(text.len());
+    for (idx, line) in trimmed.split('\n').enumerate() {
+        if idx == 0 {
+            unfilled.push_str(&line[options.initial_indent.len()..]);
+        } else {
+            unfilled.push(' ');
+            unfilled.push_str(&line[options.subsequent_indent.len()..]);
+        }
+    }
+
+    unfilled.push_str(&text[trimmed.len()..]);
+    (unfilled, options)
+}
+
+/// Refill a paragraph of wrapped text with a new width.
+///
+/// This function will first use the [`unfill`] function to remove
+/// newlines from the text. Afterwards the text is filled again using
+/// the [`fill`] function.
+///
+/// The `new_width_or_options` argument specify the new width and can
+/// specify other options as well — except for
+/// [`Options::initial_indent`] and [`Options::subsequent_indent`],
+/// which are deduced from `filled_text`.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::refill;
+///
+/// // Some loosely wrapped text. The "> " prefix is recognized automatically.
+/// let text = "\
+/// > Memory
+/// > safety without garbage
+/// > collection.
+/// ";
+///
+/// assert_eq!(refill(text, 20), "\
+/// > Memory safety
+/// > without garbage
+/// > collection.
+/// ");
+///
+/// assert_eq!(refill(text, 40), "\
+/// > Memory safety without garbage
+/// > collection.
+/// ");
+///
+/// assert_eq!(refill(text, 60), "\
+/// > Memory safety without garbage collection.
+/// ");
+/// ```
+///
+/// You can also reshape bullet points:
+///
+/// ```
+/// use textwrap::refill;
+///
+/// let text = "\
+/// - This is my
+///   list item.
+/// ";
+///
+/// assert_eq!(refill(text, 20), "\
+/// - This is my list
+///   item.
+/// ");
+/// ```
+pub fn refill<'a, Opt>(filled_text: &str, new_width_or_options: Opt) -> String
+where
+    Opt: Into<Options<'a>>,
+{
+    let trimmed = filled_text.trim_end_matches('\n');
+    let (text, options) = unfill(trimmed);
+    let mut new_options = new_width_or_options.into();
+    new_options.initial_indent = options.initial_indent;
+    new_options.subsequent_indent = options.subsequent_indent;
+    let mut refilled = fill(&text, new_options);
+    refilled.push_str(&filled_text[trimmed.len()..]);
+    refilled
+}
+
+/// Wrap a line of text at a given width.
+///
+/// The result is a vector of lines, each line is of type [`Cow<'_,
+/// str>`](Cow), which means that the line will borrow from the input
+/// `&str` if possible. The lines do not have trailing whitespace,
+/// including a final `'\n'`. Please use the [`fill`] function if you
+/// need a [`String`] instead.
+///
+/// The easiest way to use this function is to pass an integer for
+/// `width_or_options`:
+///
+/// ```
+/// use textwrap::wrap;
+///
+/// let lines = wrap("Memory safety without garbage collection.", 15);
+/// assert_eq!(lines, &[
+///     "Memory safety",
+///     "without garbage",
+///     "collection.",
+/// ]);
+/// ```
+///
+/// If you need to customize the wrapping, you can pass an [`Options`]
+/// instead of an `usize`:
+///
+/// ```
+/// use textwrap::{wrap, Options};
+///
+/// let options = Options::new(15)
+///     .initial_indent("- ")
+///     .subsequent_indent("  ");
+/// let lines = wrap("Memory safety without garbage collection.", &options);
+/// assert_eq!(lines, &[
+///     "- Memory safety",
+///     "  without",
+///     "  garbage",
+///     "  collection.",
+/// ]);
+/// ```
+///
+/// # Optimal-Fit Wrapping
+///
+/// By default, `wrap` will try to ensure an even right margin by
+/// finding breaks which avoid short lines. We call this an
+/// “optimal-fit algorithm” since the line breaks are computed by
+/// considering all possible line breaks. The alternative is a
+/// “first-fit algorithm” which simply accumulates words until they no
+/// longer fit on the line.
+///
+/// As an example, using the first-fit algorithm to wrap the famous
+/// Hamlet quote “To be, or not to be: that is the question” in a
+/// narrow column with room for only 10 characters looks like this:
+///
+/// ```
+/// # use textwrap::{WrapAlgorithm::FirstFit, Options, wrap};
+/// #
+/// # let lines = wrap("To be, or not to be: that is the question",
+/// #                  Options::new(10).wrap_algorithm(FirstFit));
+/// # assert_eq!(lines.join("\n") + "\n", "\
+/// To be, or
+/// not to be:
+/// that is
+/// the
+/// question
+/// # ");
+/// ```
+///
+/// Notice how the second to last line is quite narrow because
+/// “question” was too large to fit? The greedy first-fit algorithm
+/// doesn’t look ahead, so it has no other option than to put
+/// “question” onto its own line.
+///
+/// With the optimal-fit wrapping algorithm, the previous lines are
+/// shortened slightly in order to make the word “is” go into the
+/// second last line:
+///
+/// ```
+/// # #[cfg(feature = "smawk")] {
+/// # use textwrap::{Options, WrapAlgorithm, wrap};
+/// #
+/// # let lines = wrap(
+/// #     "To be, or not to be: that is the question",
+/// #     Options::new(10).wrap_algorithm(WrapAlgorithm::new_optimal_fit())
+/// # );
+/// # assert_eq!(lines.join("\n") + "\n", "\
+/// To be,
+/// or not to
+/// be: that
+/// is the
+/// question
+/// # "); }
+/// ```
+///
+/// Please see [`WrapAlgorithm`] for details on the choices.
+///
+/// # Examples
+///
+/// The returned iterator yields lines of type `Cow<'_, str>`. If
+/// possible, the wrapped lines will borrow from the input string. As
+/// an example, a hanging indentation, the first line can borrow from
+/// the input, but the subsequent lines become owned strings:
+///
+/// ```
+/// use std::borrow::Cow::{Borrowed, Owned};
+/// use textwrap::{wrap, Options};
+///
+/// let options = Options::new(15).subsequent_indent("....");
+/// let lines = wrap("Wrapping text all day long.", &options);
+/// let annotated = lines
+///     .iter()
+///     .map(|line| match line {
+///         Borrowed(text) => format!("[Borrowed] {}", text),
+///         Owned(text) => format!("[Owned]    {}", text),
+///     })
+///     .collect::<Vec<_>>();
+/// assert_eq!(
+///     annotated,
+///     &[
+///         "[Borrowed] Wrapping text",
+///         "[Owned]    ....all day",
+///         "[Owned]    ....long.",
+///     ]
+/// );
+/// ```
+///
+/// ## Leading and Trailing Whitespace
+///
+/// As a rule, leading whitespace (indentation) is preserved and
+/// trailing whitespace is discarded.
+///
+/// In more details, when wrapping words into lines, words are found
+/// by splitting the input text on space characters. One or more
+/// spaces (shown here as “␣”) are attached to the end of each word:
+///
+/// ```text
+/// "Foo␣␣␣bar␣baz" -> ["Foo␣␣␣", "bar␣", "baz"]
+/// ```
+///
+/// These words are then put into lines. The interword whitespace is
+/// preserved, unless the lines are wrapped so that the `"Foo␣␣␣"`
+/// word falls at the end of a line:
+///
+/// ```
+/// use textwrap::wrap;
+///
+/// assert_eq!(wrap("Foo   bar baz", 10), vec!["Foo   bar", "baz"]);
+/// assert_eq!(wrap("Foo   bar baz", 8), vec!["Foo", "bar baz"]);
+/// ```
+///
+/// Notice how the trailing whitespace is removed in both case: in the
+/// first example, `"bar␣"` becomes `"bar"` and in the second case
+/// `"Foo␣␣␣"` becomes `"Foo"`.
+///
+/// Leading whitespace is preserved when the following word fits on
+/// the first line. To understand this, consider how words are found
+/// in a text with leading spaces:
+///
+/// ```text
+/// "␣␣foo␣bar" -> ["␣␣", "foo␣", "bar"]
+/// ```
+///
+/// When put into lines, the indentation is preserved if `"foo"` fits
+/// on the first line, otherwise you end up with an empty line:
+///
+/// ```
+/// use textwrap::wrap;
+///
+/// assert_eq!(wrap("  foo bar", 8), vec!["  foo", "bar"]);
+/// assert_eq!(wrap("  foo bar", 4), vec!["", "foo", "bar"]);
+/// ```
+pub fn wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec<Cow<'_, str>>
+where
+    Opt: Into<Options<'a>>,
+{
+    let options = width_or_options.into();
+
+    let initial_width = options
+        .width
+        .saturating_sub(core::display_width(options.initial_indent));
+    let subsequent_width = options
+        .width
+        .saturating_sub(core::display_width(options.subsequent_indent));
+
+    let mut lines = Vec::new();
+    for line in text.split('\n') {
+        let words = options.word_separator.find_words(line);
+        let split_words = word_splitters::split_words(words, &options.word_splitter);
+        let broken_words = if options.break_words {
+            let mut broken_words = core::break_words(split_words, subsequent_width);
+            if !options.initial_indent.is_empty() {
+                // Without this, the first word will always go into
+                // the first line. However, since we break words based
+                // on the _second_ line width, it can be wrong to
+                // unconditionally put the first word onto the first
+                // line. An empty zero-width word fixed this.
+                broken_words.insert(0, core::Word::from(""));
+            }
+            broken_words
+        } else {
+            split_words.collect::<Vec<_>>()
+        };
+
+        let line_widths = [initial_width, subsequent_width];
+        let wrapped_words = options.wrap_algorithm.wrap(&broken_words, &line_widths);
+
+        let mut idx = 0;
+        for words in wrapped_words {
+            let last_word = match words.last() {
+                None => {
+                    lines.push(Cow::from(""));
+                    continue;
+                }
+                Some(word) => word,
+            };
+
+            // We assume here that all words are contiguous in `line`.
+            // That is, the sum of their lengths should add up to the
+            // length of `line`.
+            let len = words
+                .iter()
+                .map(|word| word.len() + word.whitespace.len())
+                .sum::<usize>()
+                - last_word.whitespace.len();
+
+            // The result is owned if we have indentation, otherwise
+            // we can simply borrow an empty string.
+            let mut result = if lines.is_empty() && !options.initial_indent.is_empty() {
+                Cow::Owned(options.initial_indent.to_owned())
+            } else if !lines.is_empty() && !options.subsequent_indent.is_empty() {
+                Cow::Owned(options.subsequent_indent.to_owned())
+            } else {
+                // We can use an empty string here since string
+                // concatenation for `Cow` preserves a borrowed value
+                // when either side is empty.
+                Cow::from("")
+            };
+
+            result += &line[idx..idx + len];
+
+            if !last_word.penalty.is_empty() {
+                result.to_mut().push_str(last_word.penalty);
+            }
+
+            lines.push(result);
+
+            // Advance by the length of `result`, plus the length of
+            // `last_word.whitespace` -- even if we had a penalty, we
+            // need to skip over the whitespace.
+            idx += len + last_word.whitespace.len();
+        }
+    }
+
+    lines
+}
+
+/// Wrap text into columns with a given total width.
+///
+/// The `left_gap`, `middle_gap` and `right_gap` arguments specify the
+/// strings to insert before, between, and after the columns. The
+/// total width of all columns and all gaps is specified using the
+/// `total_width_or_options` argument. This argument can simply be an
+/// integer if you want to use default settings when wrapping, or it
+/// can be a [`Options`] value if you want to customize the wrapping.
+///
+/// If the columns are narrow, it is recommended to set
+/// [`Options::break_words`] to `true` to prevent words from
+/// protruding into the margins.
+///
+/// The per-column width is computed like this:
+///
+/// ```
+/// # let (left_gap, middle_gap, right_gap) = ("", "", "");
+/// # let columns = 2;
+/// # let options = textwrap::Options::new(80);
+/// let inner_width = options.width
+///     - textwrap::core::display_width(left_gap)
+///     - textwrap::core::display_width(right_gap)
+///     - textwrap::core::display_width(middle_gap) * (columns - 1);
+/// let column_width = inner_width / columns;
+/// ```
+///
+/// The `text` is wrapped using [`wrap`] and the given `options`
+/// argument, but the width is overwritten to the computed
+/// `column_width`.
+///
+/// # Panics
+///
+/// Panics if `columns` is zero.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::wrap_columns;
+///
+/// let text = "\
+/// This is an example text, which is wrapped into three columns. \
+/// Notice how the final column can be shorter than the others.";
+///
+/// #[cfg(feature = "smawk")]
+/// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"),
+///            vec!["| This is       | into three    | column can be  |",
+///                 "| an example    | columns.      | shorter than   |",
+///                 "| text, which   | Notice how    | the others.    |",
+///                 "| is wrapped    | the final     |                |"]);
+///
+/// // Without the `smawk` feature, the middle column is a little more uneven:
+/// #[cfg(not(feature = "smawk"))]
+/// assert_eq!(wrap_columns(text, 3, 50, "| ", " | ", " |"),
+///            vec!["| This is an    | three         | column can be  |",
+///                 "| example text, | columns.      | shorter than   |",
+///                 "| which is      | Notice how    | the others.    |",
+///                 "| wrapped into  | the final     |                |"]);
+pub fn wrap_columns<'a, Opt>(
+    text: &str,
+    columns: usize,
+    total_width_or_options: Opt,
+    left_gap: &str,
+    middle_gap: &str,
+    right_gap: &str,
+) -> Vec<String>
+where
+    Opt: Into<Options<'a>>,
+{
+    assert!(columns > 0);
+
+    let mut options = total_width_or_options.into();
+
+    let inner_width = options
+        .width
+        .saturating_sub(core::display_width(left_gap))
+        .saturating_sub(core::display_width(right_gap))
+        .saturating_sub(core::display_width(middle_gap) * (columns - 1));
+
+    let column_width = std::cmp::max(inner_width / columns, 1);
+    options.width = column_width;
+    let last_column_padding = " ".repeat(inner_width % column_width);
+    let wrapped_lines = wrap(text, options);
+    let lines_per_column =
+        wrapped_lines.len() / columns + usize::from(wrapped_lines.len() % columns > 0);
+    let mut lines = Vec::new();
+    for line_no in 0..lines_per_column {
+        let mut line = String::from(left_gap);
+        for column_no in 0..columns {
+            match wrapped_lines.get(line_no + column_no * lines_per_column) {
+                Some(column_line) => {
+                    line.push_str(column_line);
+                    line.push_str(&" ".repeat(column_width - core::display_width(column_line)));
+                }
+                None => {
+                    line.push_str(&" ".repeat(column_width));
+                }
+            }
+            if column_no == columns - 1 {
+                line.push_str(&last_column_padding);
+            } else {
+                line.push_str(middle_gap);
+            }
+        }
+        line.push_str(right_gap);
+        lines.push(line);
+    }
+
+    lines
+}
+
+/// Fill `text` in-place without reallocating the input string.
+///
+/// This function works by modifying the input string: some `' '`
+/// characters will be replaced by `'\n'` characters. The rest of the
+/// text remains untouched.
+///
+/// Since we can only replace existing whitespace in the input with
+/// `'\n'`, we cannot do hyphenation nor can we split words longer
+/// than the line width. We also need to use `AsciiSpace` as the word
+/// separator since we need `' '` characters between words in order to
+/// replace some of them with a `'\n'`. Indentation is also ruled out.
+/// In other words, `fill_inplace(width)` behaves as if you had called
+/// [`fill`] with these options:
+///
+/// ```
+/// # use textwrap::{core, Options, WordSplitter, WordSeparator, WrapAlgorithm};
+/// # let width = 80;
+/// Options {
+///     width: width,
+///     initial_indent: "",
+///     subsequent_indent: "",
+///     break_words: false,
+///     word_separator: WordSeparator::AsciiSpace,
+///     wrap_algorithm: WrapAlgorithm::FirstFit,
+///     word_splitter: WordSplitter::NoHyphenation,
+/// };
+/// ```
+///
+/// The wrap algorithm is [`WrapAlgorithm::FirstFit`] since this
+/// is the fastest algorithm — and the main reason to use
+/// `fill_inplace` is to get the string broken into newlines as fast
+/// as possible.
+///
+/// A last difference is that (unlike [`fill`]) `fill_inplace` can
+/// leave trailing whitespace on lines. This is because we wrap by
+/// inserting a `'\n'` at the final whitespace in the input string:
+///
+/// ```
+/// let mut text = String::from("Hello   World!");
+/// textwrap::fill_inplace(&mut text, 10);
+/// assert_eq!(text, "Hello  \nWorld!");
+/// ```
+///
+/// If we didn't do this, the word `World!` would end up being
+/// indented. You can avoid this if you make sure that your input text
+/// has no double spaces.
+///
+/// # Performance
+///
+/// In benchmarks, `fill_inplace` is about twice as fast as [`fill`].
+/// Please see the [`linear`
+/// benchmark](https://github.com/mgeisler/textwrap/blob/master/benches/linear.rs)
+/// for details.
+pub fn fill_inplace(text: &mut String, width: usize) {
+    let mut indices = Vec::new();
+
+    let mut offset = 0;
+    for line in text.split('\n') {
+        let words = WordSeparator::AsciiSpace
+            .find_words(line)
+            .collect::<Vec<_>>();
+        let wrapped_words = wrap_algorithms::wrap_first_fit(&words, &[width as f64]);
+
+        let mut line_offset = offset;
+        for words in &wrapped_words[..wrapped_words.len() - 1] {
+            let line_len = words
+                .iter()
+                .map(|word| word.len() + word.whitespace.len())
+                .sum::<usize>();
+
+            line_offset += line_len;
+            // We've advanced past all ' ' characters -- want to move
+            // one ' ' backwards and insert our '\n' there.
+            indices.push(line_offset - 1);
+        }
+
+        // Advance past entire line, plus the '\n' which was removed
+        // by the split call above.
+        offset += line.len() + 1;
+    }
+
+    let mut bytes = std::mem::take(text).into_bytes();
+    for idx in indices {
+        bytes[idx] = b'\n';
+    }
+    *text = String::from_utf8(bytes).unwrap();
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[cfg(feature = "hyphenation")]
+    use hyphenation::{Language, Load, Standard};
+
+    #[test]
+    fn options_agree_with_usize() {
+        let opt_usize = Options::from(42_usize);
+        let opt_options = Options::new(42);
+
+        assert_eq!(opt_usize.width, opt_options.width);
+        assert_eq!(opt_usize.initial_indent, opt_options.initial_indent);
+        assert_eq!(opt_usize.subsequent_indent, opt_options.subsequent_indent);
+        assert_eq!(opt_usize.break_words, opt_options.break_words);
+        assert_eq!(
+            opt_usize.word_splitter.split_points("hello-world"),
+            opt_options.word_splitter.split_points("hello-world")
+        );
+    }
+
+    #[test]
+    fn no_wrap() {
+        assert_eq!(wrap("foo", 10), vec!["foo"]);
+    }
+
+    #[test]
+    fn wrap_simple() {
+        assert_eq!(wrap("foo bar baz", 5), vec!["foo", "bar", "baz"]);
+    }
+
+    #[test]
+    fn to_be_or_not() {
+        assert_eq!(
+            wrap(
+                "To be, or not to be, that is the question.",
+                Options::new(10).wrap_algorithm(WrapAlgorithm::FirstFit)
+            ),
+            vec!["To be, or", "not to be,", "that is", "the", "question."]
+        );
+    }
+
+    #[test]
+    fn multiple_words_on_first_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), vec!["foo bar"]);
+
+        let text = "Hello there! This is some English text. \
+                    It should not be wrapped given the extents below.";
+        assert_eq!(wrap(text, usize::MAX), vec![text]);
+    }
+
+    #[test]
+    fn leading_whitespace() {
+        assert_eq!(wrap("  foo bar", 6), vec!["  foo", "bar"]);
+    }
+
+    #[test]
+    fn leading_whitespace_empty_first_line() {
+        // If there is no space for the first word, the first line
+        // will be empty. This is because the string is split into
+        // words like [" ", "foobar ", "baz"], which puts "foobar " on
+        // the second line. We never output trailing whitespace
+        assert_eq!(wrap(" foobar baz", 6), vec!["", "foobar", "baz"]);
+    }
+
+    #[test]
+    fn trailing_whitespace() {
+        // 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.
+        assert_eq!(wrap("foo     bar     baz  ", 5), vec!["foo", "bar", "baz"]);
+    }
+
+    #[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.
+        let options = Options::new(1).word_separator(WordSeparator::AsciiSpace);
+        assert_eq!(wrap("x – x", options), vec!["x", "–", "x"]);
+    }
+
+    #[test]
+    fn wide_character_handling() {
+        assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]);
+        assert_eq!(
+            wrap(
+                "Hello, World!",
+                Options::new(15).word_separator(WordSeparator::AsciiSpace)
+            ),
+            vec!["Hello,", "World!"]
+        );
+
+        // Wide characters are allowed to break if the
+        // unicode-linebreak feature is enabled.
+        #[cfg(feature = "unicode-linebreak")]
+        assert_eq!(
+            wrap(
+                "Hello, World!",
+                Options::new(15).word_separator(WordSeparator::UnicodeBreakProperties)
+            ),
+            vec!["Hello, W", "orld!"]
+        );
+    }
+
+    #[test]
+    fn empty_line_is_indented() {
+        // Previously, indentation was not applied to empty lines.
+        // However, this is somewhat inconsistent and undesirable if
+        // the indentation is something like a border ("| ") which you
+        // want to apply to all lines, empty or not.
+        let options = Options::new(10).initial_indent("!!!");
+        assert_eq!(fill("", &options), "!!!");
+    }
+
+    #[test]
+    fn indent_single_line() {
+        let options = Options::new(10).initial_indent(">>>"); // No trailing space
+        assert_eq!(fill("foo", &options), ">>>foo");
+    }
+
+    #[test]
+    fn indent_first_emoji() {
+        let options = Options::new(10).initial_indent("👉👉");
+        assert_eq!(
+            wrap("x x x x x x x x x x x x x", &options),
+            vec!["👉👉x x x", "x x x x x", "x x x x x"]
+        );
+    }
+
+    #[test]
+    fn indent_multiple_lines() {
+        let options = Options::new(6).initial_indent("* ").subsequent_indent("  ");
+        assert_eq!(
+            wrap("foo bar baz", &options),
+            vec!["* foo", "  bar", "  baz"]
+        );
+    }
+
+    #[test]
+    fn indent_break_words() {
+        let options = Options::new(5).initial_indent("* ").subsequent_indent("  ");
+        assert_eq!(wrap("foobarbaz", &options), vec!["* foo", "  bar", "  baz"]);
+    }
+
+    #[test]
+    fn initial_indent_break_words() {
+        // This is a corner-case showing how the long word is broken
+        // according to the width of the subsequent lines. The first
+        // fragment of the word no longer fits on the first line,
+        // which ends up being pure indentation.
+        let options = Options::new(5).initial_indent("-->");
+        assert_eq!(wrap("foobarbaz", &options), vec!["-->", "fooba", "rbaz"]);
+    }
+
+    #[test]
+    fn hyphens() {
+        assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]);
+    }
+
+    #[test]
+    fn trailing_hyphen() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(wrap("foobar-", &options), vec!["foobar-"]);
+    }
+
+    #[test]
+    fn multiple_hyphens() {
+        assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]);
+    }
+
+    #[test]
+    fn hyphens_flag() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(
+            wrap("The --foo-bar flag.", &options),
+            vec!["The", "--foo-", "bar", "flag."]
+        );
+    }
+
+    #[test]
+    fn repeated_hyphens() {
+        let options = Options::new(4).break_words(false);
+        assert_eq!(wrap("foo--bar", &options), vec!["foo--bar"]);
+    }
+
+    #[test]
+    fn hyphens_alphanumeric() {
+        assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]);
+    }
+
+    #[test]
+    fn hyphens_non_alphanumeric() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(wrap("foo(-)bar", &options), vec!["foo(-)bar"]);
+    }
+
+    #[test]
+    fn multiple_splits() {
+        assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]);
+    }
+
+    #[test]
+    fn forced_split() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(wrap("foobar-baz", &options), vec!["foobar-", "baz"]);
+    }
+
+    #[test]
+    fn multiple_unbroken_words_issue_193() {
+        let options = Options::new(3).break_words(false);
+        assert_eq!(
+            wrap("small large tiny", &options),
+            vec!["small", "large", "tiny"]
+        );
+        assert_eq!(
+            wrap("small  large   tiny", &options),
+            vec!["small", "large", "tiny"]
+        );
+    }
+
+    #[test]
+    fn very_narrow_lines_issue_193() {
+        let options = Options::new(1).break_words(false);
+        assert_eq!(wrap("fooo x y", &options), vec!["fooo", "x", "y"]);
+        assert_eq!(wrap("fooo   x     y", &options), vec!["fooo", "x", "y"]);
+    }
+
+    #[test]
+    fn simple_hyphens() {
+        let options = Options::new(8).word_splitter(WordSplitter::HyphenSplitter);
+        assert_eq!(wrap("foo bar-baz", &options), vec!["foo bar-", "baz"]);
+    }
+
+    #[test]
+    fn no_hyphenation() {
+        let options = Options::new(8).word_splitter(WordSplitter::NoHyphenation);
+        assert_eq!(wrap("foo bar-baz", &options), vec!["foo", "bar-baz"]);
+    }
+
+    #[test]
+    #[cfg(feature = "hyphenation")]
+    fn auto_hyphenation_double_hyphenation() {
+        let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
+        let options = Options::new(10);
+        assert_eq!(
+            wrap("Internationalization", &options),
+            vec!["Internatio", "nalization"]
+        );
+
+        let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
+        assert_eq!(
+            wrap("Internationalization", &options),
+            vec!["Interna-", "tionaliza-", "tion"]
+        );
+    }
+
+    #[test]
+    #[cfg(feature = "hyphenation")]
+    fn auto_hyphenation_issue_158() {
+        let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
+        let options = Options::new(10);
+        assert_eq!(
+            wrap("participation is the key to success", &options),
+            vec!["participat", "ion is", "the key to", "success"]
+        );
+
+        let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
+        assert_eq!(
+            wrap("participation is the key to success", &options),
+            vec!["partici-", "pation is", "the key to", "success"]
+        );
+    }
+
+    #[test]
+    #[cfg(feature = "hyphenation")]
+    fn split_len_hyphenation() {
+        // Test that hyphenation takes the width of the whitespace
+        // into account.
+        let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
+        let options = Options::new(15).word_splitter(WordSplitter::Hyphenation(dictionary));
+        assert_eq!(
+            wrap("garbage   collection", &options),
+            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 options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary));
+        let lines = wrap("Internationalization", &options);
+        assert_eq!(lines, vec!["Interna-", "tionaliza-", "tion"]);
+        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 options = Options::new(8).break_words(false);
+        assert_eq!(
+            wrap("over-caffinated", &options),
+            vec!["over-", "caffinated"]
+        );
+
+        let options = options.word_splitter(WordSplitter::Hyphenation(dictionary));
+        assert_eq!(
+            wrap("over-caffinated", &options),
+            vec!["over-", "caffi-", "nated"]
+        );
+    }
+
+    #[test]
+    fn break_words() {
+        assert_eq!(wrap("foobarbaz", 3), vec!["foo", "bar", "baz"]);
+    }
+
+    #[test]
+    fn break_words_wide_characters() {
+        // Even the poor man's version of `ch_width` counts these
+        // characters as wide.
+        let options = Options::new(5).word_separator(WordSeparator::AsciiSpace);
+        assert_eq!(wrap("Hello", options), 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_long_first_word() {
+        assert_eq!(wrap("testx y", 4), vec!["test", "x y"]);
+    }
+
+    #[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 break_words_empty_lines() {
+        assert_eq!(
+            fill("foo\nbar", &Options::new(2).break_words(false)),
+            "foo\nbar"
+        );
+    }
+
+    #[test]
+    fn preserve_line_breaks() {
+        assert_eq!(fill("", 80), "");
+        assert_eq!(fill("\n", 80), "\n");
+        assert_eq!(fill("\n\n\n", 80), "\n\n\n");
+        assert_eq!(fill("test\n", 80), "test\n");
+        assert_eq!(fill("test\n\na\n\n", 80), "test\n\na\n\n");
+        assert_eq!(
+            fill(
+                "1 3 5 7\n1 3 5 7",
+                Options::new(7).wrap_algorithm(WrapAlgorithm::FirstFit)
+            ),
+            "1 3 5 7\n1 3 5 7"
+        );
+        assert_eq!(
+            fill(
+                "1 3 5 7\n1 3 5 7",
+                Options::new(5).wrap_algorithm(WrapAlgorithm::FirstFit)
+            ),
+            "1 3 5\n7\n1 3 5\n7"
+        );
+    }
+
+    #[test]
+    fn preserve_line_breaks_with_whitespace() {
+        assert_eq!(fill("  ", 80), "");
+        assert_eq!(fill("  \n  ", 80), "\n");
+        assert_eq!(fill("  \n \n  \n ", 80), "\n\n\n");
+    }
+
+    #[test]
+    fn non_breaking_space() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(fill("foo bar baz", &options), "foo bar baz");
+    }
+
+    #[test]
+    fn non_breaking_hyphen() {
+        let options = Options::new(5).break_words(false);
+        assert_eq!(fill("foo‑bar‑baz", &options), "foo‑bar‑baz");
+    }
+
+    #[test]
+    fn fill_simple() {
+        assert_eq!(fill("foo bar baz", 10), "foo bar\nbaz");
+    }
+
+    #[test]
+    fn fill_colored_text() {
+        // The words are much longer than 6 bytes, but they remain
+        // intact after filling the text.
+        let green_hello = "\u{1b}[0m\u{1b}[32mHello\u{1b}[0m";
+        let blue_world = "\u{1b}[0m\u{1b}[34mWorld!\u{1b}[0m";
+        assert_eq!(
+            fill(&(String::from(green_hello) + " " + &blue_world), 6),
+            String::from(green_hello) + "\n" + &blue_world
+        );
+    }
+
+    #[test]
+    fn fill_unicode_boundary() {
+        // https://github.com/mgeisler/textwrap/issues/390
+        fill("\u{1b}!Ͽ", 10);
+    }
+
+    #[test]
+    fn fill_inplace_empty() {
+        let mut text = String::from("");
+        fill_inplace(&mut text, 80);
+        assert_eq!(text, "");
+    }
+
+    #[test]
+    fn fill_inplace_simple() {
+        let mut text = String::from("foo bar baz");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "foo bar\nbaz");
+    }
+
+    #[test]
+    fn fill_inplace_multiple_lines() {
+        let mut text = String::from("Some text to wrap over multiple lines");
+        fill_inplace(&mut text, 12);
+        assert_eq!(text, "Some text to\nwrap over\nmultiple\nlines");
+    }
+
+    #[test]
+    fn fill_inplace_long_word() {
+        let mut text = String::from("Internationalization is hard");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "Internationalization\nis hard");
+    }
+
+    #[test]
+    fn fill_inplace_no_hyphen_splitting() {
+        let mut text = String::from("A well-chosen example");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "A\nwell-chosen\nexample");
+    }
+
+    #[test]
+    fn fill_inplace_newlines() {
+        let mut text = String::from("foo bar\n\nbaz\n\n\n");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "foo bar\n\nbaz\n\n\n");
+    }
+
+    #[test]
+    fn fill_inplace_newlines_reset_line_width() {
+        let mut text = String::from("1 3 5\n1 3 5 7 9\n1 3 5 7 9 1 3");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "1 3 5\n1 3 5 7 9\n1 3 5 7 9\n1 3");
+    }
+
+    #[test]
+    fn fill_inplace_leading_whitespace() {
+        let mut text = String::from("  foo bar baz");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "  foo bar\nbaz");
+    }
+
+    #[test]
+    fn fill_inplace_trailing_whitespace() {
+        let mut text = String::from("foo bar baz  ");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "foo bar\nbaz  ");
+    }
+
+    #[test]
+    fn fill_inplace_interior_whitespace() {
+        // To avoid an unwanted indentation of "baz", it is important
+        // to replace the final ' ' with '\n'.
+        let mut text = String::from("foo  bar    baz");
+        fill_inplace(&mut text, 10);
+        assert_eq!(text, "foo  bar   \nbaz");
+    }
+
+    #[test]
+    fn unfill_simple() {
+        let (text, options) = unfill("foo\nbar");
+        assert_eq!(text, "foo bar");
+        assert_eq!(options.width, 3);
+    }
+
+    #[test]
+    fn unfill_trailing_newlines() {
+        let (text, options) = unfill("foo\nbar\n\n\n");
+        assert_eq!(text, "foo bar\n\n\n");
+        assert_eq!(options.width, 3);
+    }
+
+    #[test]
+    fn unfill_initial_indent() {
+        let (text, options) = unfill("  foo\nbar\nbaz");
+        assert_eq!(text, "foo bar baz");
+        assert_eq!(options.width, 5);
+        assert_eq!(options.initial_indent, "  ");
+    }
+
+    #[test]
+    fn unfill_differing_indents() {
+        let (text, options) = unfill("  foo\n    bar\n  baz");
+        assert_eq!(text, "foo   bar baz");
+        assert_eq!(options.width, 7);
+        assert_eq!(options.initial_indent, "  ");
+        assert_eq!(options.subsequent_indent, "  ");
+    }
+
+    #[test]
+    fn unfill_list_item() {
+        let (text, options) = unfill("* foo\n  bar\n  baz");
+        assert_eq!(text, "foo bar baz");
+        assert_eq!(options.width, 5);
+        assert_eq!(options.initial_indent, "* ");
+        assert_eq!(options.subsequent_indent, "  ");
+    }
+
+    #[test]
+    fn unfill_multiple_char_prefix() {
+        let (text, options) = unfill("    // foo bar\n    // baz\n    // quux");
+        assert_eq!(text, "foo bar baz quux");
+        assert_eq!(options.width, 14);
+        assert_eq!(options.initial_indent, "    // ");
+        assert_eq!(options.subsequent_indent, "    // ");
+    }
+
+    #[test]
+    fn unfill_block_quote() {
+        let (text, options) = unfill("> foo\n> bar\n> baz");
+        assert_eq!(text, "foo bar baz");
+        assert_eq!(options.width, 5);
+        assert_eq!(options.initial_indent, "> ");
+        assert_eq!(options.subsequent_indent, "> ");
+    }
+
+    #[test]
+    fn unfill_whitespace() {
+        assert_eq!(unfill("foo   bar").0, "foo   bar");
+    }
+
+    #[test]
+    fn wrap_columns_empty_text() {
+        assert_eq!(wrap_columns("", 1, 10, "| ", "", " |"), vec!["|        |"]);
+    }
+
+    #[test]
+    fn wrap_columns_single_column() {
+        assert_eq!(
+            wrap_columns("Foo", 3, 30, "| ", " | ", " |"),
+            vec!["| Foo    |        |          |"]
+        );
+    }
+
+    #[test]
+    fn wrap_columns_uneven_columns() {
+        // The gaps take up a total of 5 columns, so the columns are
+        // (21 - 5)/4 = 4 columns wide:
+        assert_eq!(
+            wrap_columns("Foo Bar Baz Quux", 4, 21, "|", "|", "|"),
+            vec!["|Foo |Bar |Baz |Quux|"]
+        );
+        // As the total width increases, the last column absorbs the
+        // excess width:
+        assert_eq!(
+            wrap_columns("Foo Bar Baz Quux", 4, 24, "|", "|", "|"),
+            vec!["|Foo |Bar |Baz |Quux   |"]
+        );
+        // Finally, when the width is 25, the columns can be resized
+        // to a width of (25 - 5)/4 = 5 columns:
+        assert_eq!(
+            wrap_columns("Foo Bar Baz Quux", 4, 25, "|", "|", "|"),
+            vec!["|Foo  |Bar  |Baz  |Quux |"]
+        );
+    }
+
+    #[test]
+    #[cfg(feature = "unicode-width")]
+    fn wrap_columns_with_emojis() {
+        assert_eq!(
+            wrap_columns(
+                "Words and a few emojis 😍 wrapped in ⓶ columns",
+                2,
+                30,
+                "✨ ",
+                " ⚽ ",
+                " 👀"
+            ),
+            vec![
+                "✨ Words      ⚽ wrapped in 👀",
+                "✨ and a few  ⚽ ⓶ columns  👀",
+                "✨ emojis 😍  ⚽            👀"
+            ]
+        );
+    }
+
+    #[test]
+    fn wrap_columns_big_gaps() {
+        // The column width shrinks to 1 because the gaps take up all
+        // the space.
+        assert_eq!(
+            wrap_columns("xyz", 2, 10, "----> ", " !!! ", " <----"),
+            vec![
+                "----> x !!! z <----", //
+                "----> y !!!   <----"
+            ]
+        );
+    }
+
+    #[test]
+    #[should_panic]
+    fn wrap_columns_panic_with_zero_columns() {
+        wrap_columns("", 0, 10, "", "", "");
+    }
+}
diff --git a/vendor/textwrap/src/word_separators.rs b/vendor/textwrap/src/word_separators.rs
new file mode 100644
index 0000000..25adf31
--- /dev/null
+++ b/vendor/textwrap/src/word_separators.rs
@@ -0,0 +1,428 @@
+//! Functionality for finding words.
+//!
+//! In order to wrap text, we need to know where the legal break
+//! points are, i.e., where the words of the text are. This means that
+//! we need to define what a "word" is.
+//!
+//! A simple approach is to simply split the text on whitespace, but
+//! this does not work for East-Asian languages such as Chinese or
+//! Japanese where there are no spaces between words. Breaking a long
+//! sequence of emojis is another example where line breaks might be
+//! wanted even if there are no whitespace to be found.
+//!
+//! The [`WordSeparator`] trait is responsible for determining where
+//! there words are in a line of text. Please refer to the trait and
+//! the structs which implement it for more information.
+
+#[cfg(feature = "unicode-linebreak")]
+use crate::core::skip_ansi_escape_sequence;
+use crate::core::Word;
+
+/// Describes where words occur in a line of text.
+///
+/// The simplest approach is say that words are separated by one or
+/// more ASCII spaces (`' '`). This works for Western languages
+/// without emojis. A more complex approach is to use the Unicode line
+/// breaking algorithm, which finds break points in non-ASCII text.
+///
+/// The line breaks occur between words, please see
+/// [`WordSplitter`](crate::WordSplitter) for options of how to handle
+/// hyphenation of individual words.
+///
+/// # Examples
+///
+/// ```
+/// use textwrap::core::Word;
+/// use textwrap::WordSeparator::AsciiSpace;
+///
+/// let words = AsciiSpace.find_words("Hello World!").collect::<Vec<_>>();
+/// assert_eq!(words, vec![Word::from("Hello "), Word::from("World!")]);
+/// ```
+#[derive(Clone, Copy)]
+pub enum WordSeparator {
+    /// Find words by splitting on runs of `' '` characters.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::core::Word;
+    /// use textwrap::WordSeparator::AsciiSpace;
+    ///
+    /// let words = AsciiSpace.find_words("Hello   World!").collect::<Vec<_>>();
+    /// assert_eq!(words, vec![Word::from("Hello   "),
+    ///                        Word::from("World!")]);
+    /// ```
+    AsciiSpace,
+
+    /// Split `line` into words using Unicode break properties.
+    ///
+    /// This word separator uses the Unicode line breaking algorithm
+    /// described in [Unicode Standard Annex
+    /// #14](https://www.unicode.org/reports/tr14/) to find legal places
+    /// to break lines. There is a small difference in that the U+002D
+    /// (Hyphen-Minus) and U+00AD (Soft Hyphen) don’t create a line break:
+    /// to allow a line break at a hyphen, use
+    /// [`WordSplitter::HyphenSplitter`](crate::WordSplitter::HyphenSplitter).
+    /// Soft hyphens are not currently supported.
+    ///
+    /// # Examples
+    ///
+    /// Unlike [`WordSeparator::AsciiSpace`], the Unicode line
+    /// breaking algorithm will find line break opportunities between
+    /// some characters with no intervening whitespace:
+    ///
+    /// ```
+    /// #[cfg(feature = "unicode-linebreak")] {
+    /// use textwrap::core::Word;
+    /// use textwrap::WordSeparator::UnicodeBreakProperties;
+    ///
+    /// assert_eq!(UnicodeBreakProperties.find_words("Emojis: 😂😍").collect::<Vec<_>>(),
+    ///            vec![Word::from("Emojis: "),
+    ///                 Word::from("😂"),
+    ///                 Word::from("😍")]);
+    ///
+    /// assert_eq!(UnicodeBreakProperties.find_words("CJK: 你好").collect::<Vec<_>>(),
+    ///            vec![Word::from("CJK: "),
+    ///                 Word::from("你"),
+    ///                 Word::from("好")]);
+    /// }
+    /// ```
+    ///
+    /// A U+2060 (Word Joiner) character can be inserted if you want to
+    /// manually override the defaults and keep the characters together:
+    ///
+    /// ```
+    /// #[cfg(feature = "unicode-linebreak")] {
+    /// use textwrap::core::Word;
+    /// use textwrap::WordSeparator::UnicodeBreakProperties;
+    ///
+    /// assert_eq!(UnicodeBreakProperties.find_words("Emojis: 😂\u{2060}😍").collect::<Vec<_>>(),
+    ///            vec![Word::from("Emojis: "),
+    ///                 Word::from("😂\u{2060}😍")]);
+    /// }
+    /// ```
+    ///
+    /// The Unicode line breaking algorithm will also automatically
+    /// suppress break breaks around certain punctuation characters::
+    ///
+    /// ```
+    /// #[cfg(feature = "unicode-linebreak")] {
+    /// use textwrap::core::Word;
+    /// use textwrap::WordSeparator::UnicodeBreakProperties;
+    ///
+    /// assert_eq!(UnicodeBreakProperties.find_words("[ foo ] bar !").collect::<Vec<_>>(),
+    ///            vec![Word::from("[ foo ] "),
+    ///                 Word::from("bar !")]);
+    /// }
+    /// ```
+    #[cfg(feature = "unicode-linebreak")]
+    UnicodeBreakProperties,
+
+    /// Find words using a custom word separator
+    Custom(fn(line: &str) -> Box<dyn Iterator<Item = Word<'_>> + '_>),
+}
+
+impl std::fmt::Debug for WordSeparator {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            WordSeparator::AsciiSpace => f.write_str("AsciiSpace"),
+            #[cfg(feature = "unicode-linebreak")]
+            WordSeparator::UnicodeBreakProperties => f.write_str("UnicodeBreakProperties"),
+            WordSeparator::Custom(_) => f.write_str("Custom(...)"),
+        }
+    }
+}
+
+impl WordSeparator {
+    // This function should really return impl Iterator<Item = Word>, but
+    // this isn't possible until Rust supports higher-kinded types:
+    // https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md
+    /// Find all words in `line`.
+    pub fn find_words<'a>(&self, line: &'a str) -> Box<dyn Iterator<Item = Word<'a>> + 'a> {
+        match self {
+            WordSeparator::AsciiSpace => find_words_ascii_space(line),
+            #[cfg(feature = "unicode-linebreak")]
+            WordSeparator::UnicodeBreakProperties => find_words_unicode_break_properties(line),
+            WordSeparator::Custom(func) => func(line),
+        }
+    }
+}
+
+fn find_words_ascii_space<'a>(line: &'a str) -> Box<dyn Iterator<Item = Word<'a>> + 'a> {
+    let mut start = 0;
+    let mut in_whitespace = false;
+    let mut char_indices = line.char_indices();
+
+    Box::new(std::iter::from_fn(move || {
+        // for (idx, ch) in char_indices does not work, gives this
+        // error:
+        //
+        // > cannot move out of `char_indices`, a captured variable in
+        // > an `FnMut` closure
+        #[allow(clippy::while_let_on_iterator)]
+        while let Some((idx, ch)) = char_indices.next() {
+            if in_whitespace && ch != ' ' {
+                let word = Word::from(&line[start..idx]);
+                start = idx;
+                in_whitespace = ch == ' ';
+                return Some(word);
+            }
+
+            in_whitespace = ch == ' ';
+        }
+
+        if start < line.len() {
+            let word = Word::from(&line[start..]);
+            start = line.len();
+            return Some(word);
+        }
+
+        None
+    }))
+}
+
+// Strip all ANSI escape sequences from `text`.
+#[cfg(feature = "unicode-linebreak")]
+fn strip_ansi_escape_sequences(text: &str) -> String {
+    let mut result = String::with_capacity(text.len());
+
+    let mut chars = text.chars();
+    while let Some(ch) = chars.next() {
+        if skip_ansi_escape_sequence(ch, &mut chars) {
+            continue;
+        }
+        result.push(ch);
+    }
+
+    result
+}
+
+/// Soft hyphen, also knows as a “shy hyphen”. Should show up as ‘-’
+/// if a line is broken at this point, and otherwise be invisible.
+/// Textwrap does not currently support breaking words at soft
+/// hyphens.
+#[cfg(feature = "unicode-linebreak")]
+const SHY: char = '\u{00ad}';
+
+/// Find words in line. ANSI escape sequences are ignored in `line`.
+#[cfg(feature = "unicode-linebreak")]
+fn find_words_unicode_break_properties<'a>(
+    line: &'a str,
+) -> Box<dyn Iterator<Item = Word<'a>> + 'a> {
+    // Construct an iterator over (original index, stripped index)
+    // tuples. We find the Unicode linebreaks on a stripped string,
+    // but we need the original indices so we can form words based on
+    // the original string.
+    let mut last_stripped_idx = 0;
+    let mut char_indices = line.char_indices();
+    let mut idx_map = std::iter::from_fn(move || match char_indices.next() {
+        Some((orig_idx, ch)) => {
+            let stripped_idx = last_stripped_idx;
+            if !skip_ansi_escape_sequence(ch, &mut char_indices.by_ref().map(|(_, ch)| ch)) {
+                last_stripped_idx += ch.len_utf8();
+            }
+            Some((orig_idx, stripped_idx))
+        }
+        None => None,
+    });
+
+    let stripped = strip_ansi_escape_sequences(line);
+    let mut opportunities = unicode_linebreak::linebreaks(&stripped)
+        .filter(|(idx, _)| {
+            #[allow(clippy::match_like_matches_macro)]
+            match &stripped[..*idx].chars().next_back() {
+                // We suppress breaks at ‘-’ since we want to control
+                // this via the WordSplitter.
+                Some('-') => false,
+                // Soft hyphens are currently not supported since we
+                // require all `Word` fragments to be continuous in
+                // the input string.
+                Some(SHY) => false,
+                // Other breaks should be fine!
+                _ => true,
+            }
+        })
+        .collect::<Vec<_>>()
+        .into_iter();
+
+    // Remove final break opportunity, we will add it below using
+    // &line[start..]; This ensures that we correctly include a
+    // trailing ANSI escape sequence.
+    opportunities.next_back();
+
+    let mut start = 0;
+    Box::new(std::iter::from_fn(move || {
+        #[allow(clippy::while_let_on_iterator)]
+        while let Some((idx, _)) = opportunities.next() {
+            if let Some((orig_idx, _)) = idx_map.find(|&(_, stripped_idx)| stripped_idx == idx) {
+                let word = Word::from(&line[start..orig_idx]);
+                start = orig_idx;
+                return Some(word);
+            }
+        }
+
+        if start < line.len() {
+            let word = Word::from(&line[start..]);
+            start = line.len();
+            return Some(word);
+        }
+
+        None
+    }))
+}
+
+#[cfg(test)]
+mod tests {
+    use super::WordSeparator::*;
+    use super::*;
+
+    // Like assert_eq!, but the left expression is an iterator.
+    macro_rules! assert_iter_eq {
+        ($left:expr, $right:expr) => {
+            assert_eq!($left.collect::<Vec<_>>(), $right);
+        };
+    }
+
+    fn to_words<'a>(words: Vec<&'a str>) -> Vec<Word<'a>> {
+        words.into_iter().map(|w: &str| Word::from(&w)).collect()
+    }
+
+    macro_rules! test_find_words {
+        ($ascii_name:ident,
+         $unicode_name:ident,
+         $([ $line:expr, $ascii_words:expr, $unicode_words:expr ]),+) => {
+            #[test]
+            fn $ascii_name() {
+                $(
+                    let expected_words = to_words($ascii_words.to_vec());
+                    let actual_words = WordSeparator::AsciiSpace
+                        .find_words($line)
+                        .collect::<Vec<_>>();
+                    assert_eq!(actual_words, expected_words, "Line: {:?}", $line);
+                )+
+            }
+
+            #[test]
+            #[cfg(feature = "unicode-linebreak")]
+            fn $unicode_name() {
+                $(
+                    let expected_words = to_words($unicode_words.to_vec());
+                    let actual_words = WordSeparator::UnicodeBreakProperties
+                        .find_words($line)
+                        .collect::<Vec<_>>();
+                    assert_eq!(actual_words, expected_words, "Line: {:?}", $line);
+                )+
+            }
+        };
+    }
+
+    test_find_words!(ascii_space_empty, unicode_empty, ["", [], []]);
+
+    test_find_words!(
+        ascii_single_word,
+        unicode_single_word,
+        ["foo", ["foo"], ["foo"]]
+    );
+
+    test_find_words!(
+        ascii_two_words,
+        unicode_two_words,
+        ["foo bar", ["foo ", "bar"], ["foo ", "bar"]]
+    );
+
+    test_find_words!(
+        ascii_multiple_words,
+        unicode_multiple_words,
+        ["foo bar", ["foo ", "bar"], ["foo ", "bar"]],
+        ["x y z", ["x ", "y ", "z"], ["x ", "y ", "z"]]
+    );
+
+    test_find_words!(
+        ascii_only_whitespace,
+        unicode_only_whitespace,
+        [" ", [" "], [" "]],
+        ["    ", ["    "], ["    "]]
+    );
+
+    test_find_words!(
+        ascii_inter_word_whitespace,
+        unicode_inter_word_whitespace,
+        ["foo   bar", ["foo   ", "bar"], ["foo   ", "bar"]]
+    );
+
+    test_find_words!(
+        ascii_trailing_whitespace,
+        unicode_trailing_whitespace,
+        ["foo   ", ["foo   "], ["foo   "]]
+    );
+
+    test_find_words!(
+        ascii_leading_whitespace,
+        unicode_leading_whitespace,
+        ["   foo", ["   ", "foo"], ["   ", "foo"]]
+    );
+
+    test_find_words!(
+        ascii_multi_column_char,
+        unicode_multi_column_char,
+        ["\u{1f920}", ["\u{1f920}"], ["\u{1f920}"]] // cowboy emoji 🤠
+    );
+
+    test_find_words!(
+        ascii_hyphens,
+        unicode_hyphens,
+        ["foo-bar", ["foo-bar"], ["foo-bar"]],
+        ["foo- bar", ["foo- ", "bar"], ["foo- ", "bar"]],
+        ["foo - bar", ["foo ", "- ", "bar"], ["foo ", "- ", "bar"]],
+        ["foo -bar", ["foo ", "-bar"], ["foo ", "-bar"]]
+    );
+
+    test_find_words!(
+        ascii_newline,
+        unicode_newline,
+        ["foo\nbar", ["foo\nbar"], ["foo\n", "bar"]]
+    );
+
+    test_find_words!(
+        ascii_tab,
+        unicode_tab,
+        ["foo\tbar", ["foo\tbar"], ["foo\t", "bar"]]
+    );
+
+    test_find_words!(
+        ascii_non_breaking_space,
+        unicode_non_breaking_space,
+        ["foo\u{00A0}bar", ["foo\u{00A0}bar"], ["foo\u{00A0}bar"]]
+    );
+
+    #[test]
+    #[cfg(unix)]
+    fn find_words_colored_text() {
+        use termion::color::{Blue, Fg, Green, Reset};
+
+        let green_hello = format!("{}Hello{} ", Fg(Green), Fg(Reset));
+        let blue_world = format!("{}World!{}", Fg(Blue), Fg(Reset));
+        assert_iter_eq!(
+            AsciiSpace.find_words(&format!("{}{}", green_hello, blue_world)),
+            vec![Word::from(&green_hello), Word::from(&blue_world)]
+        );
+
+        #[cfg(feature = "unicode-linebreak")]
+        assert_iter_eq!(
+            UnicodeBreakProperties.find_words(&format!("{}{}", green_hello, blue_world)),
+            vec![Word::from(&green_hello), Word::from(&blue_world)]
+        );
+    }
+
+    #[test]
+    fn find_words_color_inside_word() {
+        let text = "foo\u{1b}[0m\u{1b}[32mbar\u{1b}[0mbaz";
+        assert_iter_eq!(AsciiSpace.find_words(&text), vec![Word::from(text)]);
+
+        #[cfg(feature = "unicode-linebreak")]
+        assert_iter_eq!(
+            UnicodeBreakProperties.find_words(&text),
+            vec![Word::from(text)]
+        );
+    }
+}
diff --git a/vendor/textwrap/src/word_splitters.rs b/vendor/textwrap/src/word_splitters.rs
new file mode 100644
index 0000000..69e246f
--- /dev/null
+++ b/vendor/textwrap/src/word_splitters.rs
@@ -0,0 +1,314 @@
+//! Word splitting functionality.
+//!
+//! To wrap text into lines, long words sometimes need to be split
+//! across lines. The [`WordSplitter`] enum defines this
+//! functionality.
+
+use crate::core::{display_width, Word};
+
+/// The `WordSplitter` enum describes where words can be split.
+///
+/// If the textwrap crate has been compiled with the `hyphenation`
+/// Cargo feature enabled, you will find a
+/// [`WordSplitter::Hyphenation`] variant. Use this struct for
+/// language-aware hyphenation:
+///
+/// ```
+/// #[cfg(feature = "hyphenation")] {
+///     use hyphenation::{Language, Load, Standard};
+///     use textwrap::{wrap, Options, WordSplitter};
+///
+///     let text = "Oxidation is the loss of electrons.";
+///     let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
+///     let options = Options::new(8).word_splitter(WordSplitter::Hyphenation(dictionary));
+///     assert_eq!(wrap(text, &options), vec!["Oxida-",
+///                                           "tion is",
+///                                           "the loss",
+///                                           "of elec-",
+///                                           "trons."]);
+/// }
+/// ```
+///
+/// Please see the documentation for the [hyphenation] crate for more
+/// details.
+///
+/// [hyphenation]: https://docs.rs/hyphenation/
+#[derive(Clone)]
+pub enum WordSplitter {
+    /// Use this as a [`Options.word_splitter`] to avoid any kind of
+    /// hyphenation:
+    ///
+    /// ```
+    /// use textwrap::{wrap, Options, WordSplitter};
+    ///
+    /// let options = Options::new(8).word_splitter(WordSplitter::NoHyphenation);
+    /// assert_eq!(wrap("foo bar-baz", &options),
+    ///            vec!["foo", "bar-baz"]);
+    /// ```
+    ///
+    /// [`Options.word_splitter`]: super::Options::word_splitter
+    NoHyphenation,
+
+    /// `HyphenSplitter` is the default `WordSplitter` used by
+    /// [`Options::new`](super::Options::new). It will split words on
+    /// existing hyphens in the word.
+    ///
+    /// It will only use hyphens that are surrounded by alphanumeric
+    /// characters, which prevents a word like `"--foo-bar"` from
+    /// being split into `"--"` and `"foo-bar"`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::WordSplitter;
+    ///
+    /// assert_eq!(WordSplitter::HyphenSplitter.split_points("--foo-bar"),
+    ///            vec![6]);
+    /// ```
+    HyphenSplitter,
+
+    /// Use a custom function as the word splitter.
+    ///
+    /// This varian lets you implement a custom word splitter using
+    /// your own function.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::WordSplitter;
+    ///
+    /// fn split_at_underscore(word: &str) -> Vec<usize> {
+    ///     word.match_indices('_').map(|(idx, _)| idx + 1).collect()
+    /// }
+    ///
+    /// let word_splitter = WordSplitter::Custom(split_at_underscore);
+    /// assert_eq!(word_splitter.split_points("a_long_identifier"),
+    ///            vec![2, 7]);
+    /// ```
+    Custom(fn(word: &str) -> Vec<usize>),
+
+    /// A hyphenation dictionary can be used to do language-specific
+    /// hyphenation using patterns from the [hyphenation] crate.
+    ///
+    /// **Note:** Only available when the `hyphenation` Cargo feature is
+    /// enabled.
+    ///
+    /// [hyphenation]: https://docs.rs/hyphenation/
+    #[cfg(feature = "hyphenation")]
+    Hyphenation(hyphenation::Standard),
+}
+
+impl std::fmt::Debug for WordSplitter {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            WordSplitter::NoHyphenation => f.write_str("NoHyphenation"),
+            WordSplitter::HyphenSplitter => f.write_str("HyphenSplitter"),
+            WordSplitter::Custom(_) => f.write_str("Custom(...)"),
+            #[cfg(feature = "hyphenation")]
+            WordSplitter::Hyphenation(dict) => write!(f, "Hyphenation({})", dict.language()),
+        }
+    }
+}
+
+impl PartialEq<WordSplitter> for WordSplitter {
+    fn eq(&self, other: &WordSplitter) -> bool {
+        match (self, other) {
+            (WordSplitter::NoHyphenation, WordSplitter::NoHyphenation) => true,
+            (WordSplitter::HyphenSplitter, WordSplitter::HyphenSplitter) => true,
+            #[cfg(feature = "hyphenation")]
+            (WordSplitter::Hyphenation(this_dict), WordSplitter::Hyphenation(other_dict)) => {
+                this_dict.language() == other_dict.language()
+            }
+            (_, _) => false,
+        }
+    }
+}
+
+impl WordSplitter {
+    /// Return all possible indices where `word` can be split.
+    ///
+    /// The indices are in the range `0..word.len()`. They point to
+    /// the index _after_ the split point, i.e., after `-` if
+    /// splitting on hyphens. This way, `word.split_at(idx)` will
+    /// break the word into two well-formed pieces.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use textwrap::WordSplitter;
+    /// assert_eq!(WordSplitter::NoHyphenation.split_points("cannot-be-split"), vec![]);
+    /// assert_eq!(WordSplitter::HyphenSplitter.split_points("can-be-split"), vec![4, 7]);
+    /// assert_eq!(WordSplitter::Custom(|word| vec![word.len()/2]).split_points("middle"), vec![3]);
+    /// ```
+    pub fn split_points(&self, word: &str) -> Vec<usize> {
+        match self {
+            WordSplitter::NoHyphenation => Vec::new(),
+            WordSplitter::HyphenSplitter => {
+                let mut splits = Vec::new();
+
+                for (idx, _) in word.match_indices('-') {
+                    // We only use hyphens that are surrounded by alphanumeric
+                    // characters. This is to avoid splitting on repeated hyphens,
+                    // such as those found in --foo-bar.
+                    let prev = word[..idx].chars().next_back();
+                    let next = word[idx + 1..].chars().next();
+
+                    if prev.filter(|ch| ch.is_alphanumeric()).is_some()
+                        && next.filter(|ch| ch.is_alphanumeric()).is_some()
+                    {
+                        splits.push(idx + 1); // +1 due to width of '-'.
+                    }
+                }
+
+                splits
+            }
+            WordSplitter::Custom(splitter_func) => splitter_func(word),
+            #[cfg(feature = "hyphenation")]
+            WordSplitter::Hyphenation(dictionary) => {
+                use hyphenation::Hyphenator;
+                dictionary.hyphenate(word).breaks
+            }
+        }
+    }
+}
+
+/// Split words into smaller words according to the split points given
+/// by `word_splitter`.
+///
+/// Note that we split all words, regardless of their length. This is
+/// to more cleanly separate the business of splitting (including
+/// automatic hyphenation) from the business of word wrapping.
+pub fn split_words<'a, I>(
+    words: I,
+    word_splitter: &'a WordSplitter,
+) -> impl Iterator<Item = Word<'a>>
+where
+    I: IntoIterator<Item = Word<'a>>,
+{
+    words.into_iter().flat_map(move |word| {
+        let mut prev = 0;
+        let mut split_points = word_splitter.split_points(&word).into_iter();
+        std::iter::from_fn(move || {
+            if let Some(idx) = split_points.next() {
+                let need_hyphen = !word[..idx].ends_with('-');
+                let w = Word {
+                    word: &word.word[prev..idx],
+                    width: display_width(&word[prev..idx]),
+                    whitespace: "",
+                    penalty: if need_hyphen { "-" } else { "" },
+                };
+                prev = idx;
+                return Some(w);
+            }
+
+            if prev < word.word.len() || prev == 0 {
+                let w = Word {
+                    word: &word.word[prev..],
+                    width: display_width(&word[prev..]),
+                    whitespace: word.whitespace,
+                    penalty: word.penalty,
+                };
+                prev = word.word.len() + 1;
+                return Some(w);
+            }
+
+            None
+        })
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // Like assert_eq!, but the left expression is an iterator.
+    macro_rules! assert_iter_eq {
+        ($left:expr, $right:expr) => {
+            assert_eq!($left.collect::<Vec<_>>(), $right);
+        };
+    }
+
+    #[test]
+    fn split_words_no_words() {
+        assert_iter_eq!(split_words(vec![], &WordSplitter::HyphenSplitter), vec![]);
+    }
+
+    #[test]
+    fn split_words_empty_word() {
+        assert_iter_eq!(
+            split_words(vec![Word::from("   ")], &WordSplitter::HyphenSplitter),
+            vec![Word::from("   ")]
+        );
+    }
+
+    #[test]
+    fn split_words_single_word() {
+        assert_iter_eq!(
+            split_words(vec![Word::from("foobar")], &WordSplitter::HyphenSplitter),
+            vec![Word::from("foobar")]
+        );
+    }
+
+    #[test]
+    fn split_words_hyphen_splitter() {
+        assert_iter_eq!(
+            split_words(vec![Word::from("foo-bar")], &WordSplitter::HyphenSplitter),
+            vec![Word::from("foo-"), Word::from("bar")]
+        );
+    }
+
+    #[test]
+    fn split_words_no_hyphenation() {
+        assert_iter_eq!(
+            split_words(vec![Word::from("foo-bar")], &WordSplitter::NoHyphenation),
+            vec![Word::from("foo-bar")]
+        );
+    }
+
+    #[test]
+    fn split_words_adds_penalty() {
+        let fixed_split_point = |_: &str| vec![3];
+
+        assert_iter_eq!(
+            split_words(
+                vec![Word::from("foobar")].into_iter(),
+                &WordSplitter::Custom(fixed_split_point)
+            ),
+            vec![
+                Word {
+                    word: "foo",
+                    width: 3,
+                    whitespace: "",
+                    penalty: "-"
+                },
+                Word {
+                    word: "bar",
+                    width: 3,
+                    whitespace: "",
+                    penalty: ""
+                }
+            ]
+        );
+
+        assert_iter_eq!(
+            split_words(
+                vec![Word::from("fo-bar")].into_iter(),
+                &WordSplitter::Custom(fixed_split_point)
+            ),
+            vec![
+                Word {
+                    word: "fo-",
+                    width: 3,
+                    whitespace: "",
+                    penalty: ""
+                },
+                Word {
+                    word: "bar",
+                    width: 3,
+                    whitespace: "",
+                    penalty: ""
+                }
+            ]
+        );
+    }
+}
diff --git a/vendor/textwrap/src/wrap_algorithms.rs b/vendor/textwrap/src/wrap_algorithms.rs
new file mode 100644
index 0000000..5ca49c3
--- /dev/null
+++ b/vendor/textwrap/src/wrap_algorithms.rs
@@ -0,0 +1,381 @@
+//! Word wrapping algorithms.
+//!
+//! After a text has been broken into words (or [`Fragment`]s), one
+//! now has to decide how to break the fragments into lines. The
+//! simplest algorithm for this is implemented by [`wrap_first_fit`]:
+//! it uses no look-ahead and simply adds fragments to the line as
+//! long as they fit. However, this can lead to poor line breaks if a
+//! large fragment almost-but-not-quite fits on a line. When that
+//! happens, the fragment is moved to the next line and it will leave
+//! behind a large gap. A more advanced algorithm, implemented by
+//! [`wrap_optimal_fit`], will take this into account. The optimal-fit
+//! algorithm considers all possible line breaks and will attempt to
+//! minimize the gaps left behind by overly short lines.
+//!
+//! While both algorithms run in linear time, the first-fit algorithm
+//! is about 4 times faster than the optimal-fit algorithm.
+
+#[cfg(feature = "smawk")]
+mod optimal_fit;
+#[cfg(feature = "smawk")]
+pub use optimal_fit::{wrap_optimal_fit, OverflowError, Penalties};
+
+use crate::core::{Fragment, Word};
+
+/// Describes how to wrap words into lines.
+///
+/// The simplest approach is to wrap words one word at a time and
+/// accept the first way of wrapping which fit
+/// ([`WrapAlgorithm::FirstFit`]). If the `smawk` Cargo feature is
+/// enabled, a more complex algorithm is available which will look at
+/// an entire paragraph at a time in order to find optimal line breaks
+/// ([`WrapAlgorithm::OptimalFit`]).
+#[derive(Clone, Copy)]
+pub enum WrapAlgorithm {
+    /// Wrap words using a fast and simple algorithm.
+    ///
+    /// This algorithm uses no look-ahead when finding line breaks.
+    /// Implemented by [`wrap_first_fit`], please see that function for
+    /// details and examples.
+    FirstFit,
+
+    /// Wrap words using an advanced algorithm with look-ahead.
+    ///
+    /// This wrapping algorithm considers the entire paragraph to find
+    /// optimal line breaks. When wrapping text, "penalties" are
+    /// assigned to line breaks based on the gaps left at the end of
+    /// lines. See [`Penalties`] for details.
+    ///
+    /// The underlying wrapping algorithm is implemented by
+    /// [`wrap_optimal_fit`], please see that function for examples.
+    ///
+    /// **Note:** Only available when the `smawk` Cargo feature is
+    /// enabled.
+    #[cfg(feature = "smawk")]
+    OptimalFit(Penalties),
+
+    /// Custom wrapping function.
+    ///
+    /// Use this if you want to implement your own wrapping algorithm.
+    /// The function can freely decide how to turn a slice of
+    /// [`Word`]s into lines.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use textwrap::core::Word;
+    /// use textwrap::{wrap, Options, WrapAlgorithm};
+    ///
+    /// fn stair<'a, 'b>(words: &'b [Word<'a>], _: &'b [usize]) -> Vec<&'b [Word<'a>]> {
+    ///     let mut lines = Vec::new();
+    ///     let mut step = 1;
+    ///     let mut start_idx = 0;
+    ///     while start_idx + step <= words.len() {
+    ///       lines.push(&words[start_idx .. start_idx+step]);
+    ///       start_idx += step;
+    ///       step += 1;
+    ///     }
+    ///     lines
+    /// }
+    ///
+    /// let options = Options::new(10).wrap_algorithm(WrapAlgorithm::Custom(stair));
+    /// assert_eq!(wrap("First, second, third, fourth, fifth, sixth", options),
+    ///            vec!["First,",
+    ///                 "second, third,",
+    ///                 "fourth, fifth, sixth"]);
+    /// ```
+    Custom(for<'a, 'b> fn(words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]>),
+}
+
+impl std::fmt::Debug for WrapAlgorithm {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            WrapAlgorithm::FirstFit => f.write_str("FirstFit"),
+            #[cfg(feature = "smawk")]
+            WrapAlgorithm::OptimalFit(penalties) => write!(f, "OptimalFit({:?})", penalties),
+            WrapAlgorithm::Custom(_) => f.write_str("Custom(...)"),
+        }
+    }
+}
+
+impl WrapAlgorithm {
+    /// Create new wrap algorithm.
+    ///
+    /// The best wrapping algorithm is used by default, i.e.,
+    /// [`WrapAlgorithm::OptimalFit`] if available, otherwise
+    /// [`WrapAlgorithm::FirstFit`].
+    pub const fn new() -> Self {
+        #[cfg(not(feature = "smawk"))]
+        {
+            WrapAlgorithm::FirstFit
+        }
+
+        #[cfg(feature = "smawk")]
+        {
+            WrapAlgorithm::new_optimal_fit()
+        }
+    }
+
+    /// New [`WrapAlgorithm::OptimalFit`] with default penalties. This
+    /// works well for monospace text.
+    ///
+    /// **Note:** Only available when the `smawk` Cargo feature is
+    /// enabled.
+    #[cfg(feature = "smawk")]
+    pub const fn new_optimal_fit() -> Self {
+        WrapAlgorithm::OptimalFit(Penalties::new())
+    }
+
+    /// Wrap words according to line widths.
+    ///
+    /// The `line_widths` slice gives the target line width for each
+    /// line (the last slice element is repeated as necessary). This
+    /// can be used to implement hanging indentation.
+    #[inline]
+    pub fn wrap<'a, 'b>(
+        &self,
+        words: &'b [Word<'a>],
+        line_widths: &'b [usize],
+    ) -> Vec<&'b [Word<'a>]> {
+        // Every integer up to 2u64.pow(f64::MANTISSA_DIGITS) = 2**53
+        // = 9_007_199_254_740_992 can be represented without loss by
+        // a f64. Larger line widths will be rounded to the nearest
+        // representable number.
+        let f64_line_widths = line_widths.iter().map(|w| *w as f64).collect::<Vec<_>>();
+
+        match self {
+            WrapAlgorithm::FirstFit => wrap_first_fit(words, &f64_line_widths),
+
+            #[cfg(feature = "smawk")]
+            WrapAlgorithm::OptimalFit(penalties) => {
+                // The computation cannnot overflow when the line
+                // widths are restricted to usize.
+                wrap_optimal_fit(words, &f64_line_widths, penalties).unwrap()
+            }
+
+            WrapAlgorithm::Custom(func) => func(words, line_widths),
+        }
+    }
+}
+
+impl Default for WrapAlgorithm {
+    fn default() -> Self {
+        WrapAlgorithm::new()
+    }
+}
+
+/// Wrap abstract fragments into lines with a first-fit algorithm.
+///
+/// The `line_widths` slice gives the target line width for each line
+/// (the last slice element is repeated as necessary). This can be
+/// used to implement hanging indentation.
+///
+/// The fragments must already have been split into the desired
+/// widths, this function will not (and cannot) attempt to split them
+/// further when arranging them into lines.
+///
+/// # First-Fit Algorithm
+///
+/// This implements a simple “greedy” algorithm: accumulate fragments
+/// one by one and when a fragment no longer fits, start a new line.
+/// There is no look-ahead, we simply take first fit of the fragments
+/// we find.
+///
+/// While fast and predictable, this algorithm can produce poor line
+/// breaks when a long fragment is moved to a new line, leaving behind
+/// a large gap:
+///
+/// ```
+/// use textwrap::core::Word;
+/// use textwrap::wrap_algorithms::wrap_first_fit;
+/// use textwrap::WordSeparator;
+///
+/// // Helper to convert wrapped lines to a Vec<String>.
+/// fn lines_to_strings(lines: Vec<&[Word<'_>]>) -> Vec<String> {
+///     lines.iter().map(|line| {
+///         line.iter().map(|word| &**word).collect::<Vec<_>>().join(" ")
+///     }).collect::<Vec<_>>()
+/// }
+///
+/// let text = "These few words will unfortunately not wrap nicely.";
+/// let words = WordSeparator::AsciiSpace.find_words(text).collect::<Vec<_>>();
+/// assert_eq!(lines_to_strings(wrap_first_fit(&words, &[15.0])),
+///            vec!["These few words",
+///                 "will",  // <-- short line
+///                 "unfortunately",
+///                 "not wrap",
+///                 "nicely."]);
+///
+/// // We can avoid the short line if we look ahead:
+/// #[cfg(feature = "smawk")]
+/// use textwrap::wrap_algorithms::{wrap_optimal_fit, Penalties};
+/// #[cfg(feature = "smawk")]
+/// assert_eq!(lines_to_strings(wrap_optimal_fit(&words, &[15.0], &Penalties::new()).unwrap()),
+///            vec!["These few",
+///                 "words will",
+///                 "unfortunately",
+///                 "not wrap",
+///                 "nicely."]);
+/// ```
+///
+/// The [`wrap_optimal_fit`] function was used above to get better
+/// line breaks. It uses an advanced algorithm which tries to avoid
+/// short lines. This function is about 4 times faster than
+/// [`wrap_optimal_fit`].
+///
+/// # Examples
+///
+/// Imagine you're building a house site and you have a number of
+/// tasks you need to execute. Things like pour foundation, complete
+/// framing, install plumbing, electric cabling, install insulation.
+///
+/// The construction workers can only work during daytime, so they
+/// need to pack up everything at night. Because they need to secure
+/// their tools and move machines back to the garage, this process
+/// takes much more time than the time it would take them to simply
+/// switch to another task.
+///
+/// You would like to make a list of tasks to execute every day based
+/// on your estimates. You can model this with a program like this:
+///
+/// ```
+/// use textwrap::core::{Fragment, Word};
+/// use textwrap::wrap_algorithms::wrap_first_fit;
+///
+/// #[derive(Debug)]
+/// struct Task<'a> {
+///     name: &'a str,
+///     hours: f64,   // Time needed to complete task.
+///     sweep: f64,   // Time needed for a quick sweep after task during the day.
+///     cleanup: f64, // Time needed for full cleanup if day ends with this task.
+/// }
+///
+/// impl Fragment for Task<'_> {
+///     fn width(&self) -> f64 { self.hours }
+///     fn whitespace_width(&self) -> f64 { self.sweep }
+///     fn penalty_width(&self) -> f64 { self.cleanup }
+/// }
+///
+/// // The morning tasks
+/// let tasks = vec![
+///     Task { name: "Foundation",  hours: 4.0, sweep: 2.0, cleanup: 3.0 },
+///     Task { name: "Framing",     hours: 3.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Plumbing",    hours: 2.0, sweep: 2.0, cleanup: 2.0 },
+///     Task { name: "Electrical",  hours: 2.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Insulation",  hours: 2.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Drywall",     hours: 3.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Floors",      hours: 3.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Countertops", hours: 1.0, sweep: 1.0, cleanup: 2.0 },
+///     Task { name: "Bathrooms",   hours: 2.0, sweep: 1.0, cleanup: 2.0 },
+/// ];
+///
+/// // Fill tasks into days, taking `day_length` into account. The
+/// // output shows the hours worked per day along with the names of
+/// // the tasks for that day.
+/// fn assign_days<'a>(tasks: &[Task<'a>], day_length: f64) -> Vec<(f64, Vec<&'a str>)> {
+///     let mut days = Vec::new();
+///     // Assign tasks to days. The assignment is a vector of slices,
+///     // with a slice per day.
+///     let assigned_days: Vec<&[Task<'a>]> = wrap_first_fit(&tasks, &[day_length]);
+///     for day in assigned_days.iter() {
+///         let last = day.last().unwrap();
+///         let work_hours: f64 = day.iter().map(|t| t.hours + t.sweep).sum();
+///         let names = day.iter().map(|t| t.name).collect::<Vec<_>>();
+///         days.push((work_hours - last.sweep + last.cleanup, names));
+///     }
+///     days
+/// }
+///
+/// // With a single crew working 8 hours a day:
+/// assert_eq!(
+///     assign_days(&tasks, 8.0),
+///     [
+///         (7.0, vec!["Foundation"]),
+///         (8.0, vec!["Framing", "Plumbing"]),
+///         (7.0, vec!["Electrical", "Insulation"]),
+///         (5.0, vec!["Drywall"]),
+///         (7.0, vec!["Floors", "Countertops"]),
+///         (4.0, vec!["Bathrooms"]),
+///     ]
+/// );
+///
+/// // With two crews working in shifts, 16 hours a day:
+/// assert_eq!(
+///     assign_days(&tasks, 16.0),
+///     [
+///         (14.0, vec!["Foundation", "Framing", "Plumbing"]),
+///         (15.0, vec!["Electrical", "Insulation", "Drywall", "Floors"]),
+///         (6.0, vec!["Countertops", "Bathrooms"]),
+///     ]
+/// );
+/// ```
+///
+/// Apologies to anyone who actually knows how to build a house and
+/// knows how long each step takes :-)
+pub fn wrap_first_fit<'a, 'b, T: Fragment>(
+    fragments: &'a [T],
+    line_widths: &'b [f64],
+) -> Vec<&'a [T]> {
+    // The final line width is used for all remaining lines.
+    let default_line_width = line_widths.last().copied().unwrap_or(0.0);
+    let mut lines = Vec::new();
+    let mut start = 0;
+    let mut width = 0.0;
+
+    for (idx, fragment) in fragments.iter().enumerate() {
+        let line_width = line_widths
+            .get(lines.len())
+            .copied()
+            .unwrap_or(default_line_width);
+        if width + fragment.width() + fragment.penalty_width() > line_width && idx > start {
+            lines.push(&fragments[start..idx]);
+            start = idx;
+            width = 0.0;
+        }
+        width += fragment.width() + fragment.whitespace_width();
+    }
+    lines.push(&fragments[start..]);
+    lines
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[derive(Debug, PartialEq)]
+    struct Word(f64);
+
+    #[rustfmt::skip]
+    impl Fragment for Word {
+        fn width(&self) -> f64 { self.0 }
+        fn whitespace_width(&self) -> f64 { 1.0 }
+        fn penalty_width(&self) -> f64 { 0.0 }
+    }
+
+    #[test]
+    fn wrap_string_longer_than_f64() {
+        let words = vec![
+            Word(1e307),
+            Word(2e307),
+            Word(3e307),
+            Word(4e307),
+            Word(5e307),
+            Word(6e307),
+        ];
+        // Wrap at just under f64::MAX (~19e307). The tiny
+        // whitespace_widths disappear because of loss of precision.
+        assert_eq!(
+            wrap_first_fit(&words, &[15e307]),
+            &[
+                vec![
+                    Word(1e307),
+                    Word(2e307),
+                    Word(3e307),
+                    Word(4e307),
+                    Word(5e307)
+                ],
+                vec![Word(6e307)]
+            ]
+        );
+    }
+}
diff --git a/vendor/textwrap/src/wrap_algorithms/optimal_fit.rs b/vendor/textwrap/src/wrap_algorithms/optimal_fit.rs
new file mode 100644
index 0000000..0625e28
--- /dev/null
+++ b/vendor/textwrap/src/wrap_algorithms/optimal_fit.rs
@@ -0,0 +1,433 @@
+use std::cell::RefCell;
+
+use crate::core::Fragment;
+
+/// Penalties for
+/// [`WrapAlgorithm::OptimalFit`](crate::WrapAlgorithm::OptimalFit)
+/// and [`wrap_optimal_fit`].
+///
+/// This wrapping algorithm in [`wrap_optimal_fit`] considers the
+/// entire paragraph to find optimal line breaks. When wrapping text,
+/// "penalties" are assigned to line breaks based on the gaps left at
+/// the end of lines. The penalties are given by this struct, with
+/// [`Penalties::default`] assigning penalties that work well for
+/// monospace text.
+///
+/// If you are wrapping proportional text, you are advised to assign
+/// your own penalties according to your font size. See the individual
+/// penalties below for details.
+///
+/// **Note:** Only available when the `smawk` Cargo feature is
+/// enabled.
+#[derive(Clone, Copy, Debug)]
+pub struct Penalties {
+    /// Per-line penalty. This is added for every line, which makes it
+    /// expensive to output more lines than the minimum required.
+    pub nline_penalty: usize,
+
+    /// Per-character cost for lines that overflow the target line width.
+    ///
+    /// With a default value of 50², every single character costs as
+    /// much as leaving a gap of 50 characters behind. This is because
+    /// we assign as cost of `gap * gap` to a short line. When
+    /// wrapping monospace text, we can overflow the line by 1
+    /// character in extreme cases:
+    ///
+    /// ```
+    /// use textwrap::core::Word;
+    /// use textwrap::wrap_algorithms::{wrap_optimal_fit, Penalties};
+    ///
+    /// let short = "foo ";
+    /// let long = "x".repeat(50);
+    /// let length = (short.len() + long.len()) as f64;
+    /// let fragments = vec![Word::from(short), Word::from(&long)];
+    /// let penalties = Penalties::new();
+    ///
+    /// // Perfect fit, both words are on a single line with no overflow.
+    /// let wrapped = wrap_optimal_fit(&fragments, &[length], &penalties).unwrap();
+    /// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]);
+    ///
+    /// // The words no longer fit, yet we get a single line back. While
+    /// // the cost of overflow (`1 * 2500`) is the same as the cost of the
+    /// // gap (`50 * 50 = 2500`), the tie is broken by `nline_penalty`
+    /// // which makes it cheaper to overflow than to use two lines.
+    /// let wrapped = wrap_optimal_fit(&fragments, &[length - 1.0], &penalties).unwrap();
+    /// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]);
+    ///
+    /// // The cost of overflow would be 2 * 2500, whereas the cost of
+    /// // the gap is only `49 * 49 + nline_penalty = 2401 + 1000 =
+    /// // 3401`. We therefore get two lines.
+    /// let wrapped = wrap_optimal_fit(&fragments, &[length - 2.0], &penalties).unwrap();
+    /// assert_eq!(wrapped, vec![&[Word::from(short)],
+    ///                          &[Word::from(&long)]]);
+    /// ```
+    ///
+    /// This only happens if the overflowing word is 50 characters
+    /// long _and_ if the word overflows the line by exactly one
+    /// character. If it overflows by more than one character, the
+    /// overflow penalty will quickly outgrow the cost of the gap, as
+    /// seen above.
+    pub overflow_penalty: usize,
+
+    /// When should the a single word on the last line be considered
+    /// "too short"?
+    ///
+    /// If the last line of the text consist of a single word and if
+    /// this word is shorter than `1 / short_last_line_fraction` of
+    /// the line width, then the final line will be considered "short"
+    /// and `short_last_line_penalty` is added as an extra penalty.
+    ///
+    /// The effect of this is to avoid a final line consisting of a
+    /// single small word. For example, with a
+    /// `short_last_line_penalty` of 25 (the default), a gap of up to
+    /// 5 columns will be seen as more desirable than having a final
+    /// short line.
+    ///
+    /// ## Examples
+    ///
+    /// ```
+    /// use textwrap::{wrap, wrap_algorithms, Options, WrapAlgorithm};
+    ///
+    /// let text = "This is a demo of the short last line penalty.";
+    ///
+    /// // The first-fit algorithm leaves a single short word on the last line:
+    /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::FirstFit)),
+    ///            vec!["This is a demo of the short last line",
+    ///                 "penalty."]);
+    ///
+    /// #[cfg(feature = "smawk")] {
+    /// let mut penalties = wrap_algorithms::Penalties::new();
+    ///
+    /// // Since "penalty." is shorter than 25% of the line width, the
+    /// // optimal-fit algorithm adds a penalty of 25. This is enough
+    /// // to move "line " down:
+    /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))),
+    ///            vec!["This is a demo of the short last",
+    ///                 "line penalty."]);
+    ///
+    /// // We can change the meaning of "short" lines. Here, only words
+    /// // shorter than 1/10th of the line width will be considered short:
+    /// penalties.short_last_line_fraction = 10;
+    /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))),
+    ///            vec!["This is a demo of the short last line",
+    ///                 "penalty."]);
+    ///
+    /// // If desired, the penalty can also be disabled:
+    /// penalties.short_last_line_fraction = 4;
+    /// penalties.short_last_line_penalty = 0;
+    /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))),
+    ///            vec!["This is a demo of the short last line",
+    ///                 "penalty."]);
+    /// }
+    /// ```
+    pub short_last_line_fraction: usize,
+
+    /// Penalty for a last line with a single short word.
+    ///
+    /// Set this to zero if you do not want to penalize short last lines.
+    pub short_last_line_penalty: usize,
+
+    /// Penalty for lines ending with a hyphen.
+    pub hyphen_penalty: usize,
+}
+
+impl Penalties {
+    /// Default penalties for monospace text.
+    ///
+    /// The penalties here work well for monospace text. This is
+    /// because they expect the gaps at the end of lines to be roughly
+    /// in the range `0..100`. If the gaps are larger, the
+    /// `overflow_penalty` and `hyphen_penalty` become insignificant.
+    pub const fn new() -> Self {
+        Penalties {
+            nline_penalty: 1000,
+            overflow_penalty: 50 * 50,
+            short_last_line_fraction: 4,
+            short_last_line_penalty: 25,
+            hyphen_penalty: 25,
+        }
+    }
+}
+
+impl Default for Penalties {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Cache for line numbers. This is necessary to avoid a O(n**2)
+/// behavior when computing line numbers in [`wrap_optimal_fit`].
+struct LineNumbers {
+    line_numbers: RefCell<Vec<usize>>,
+}
+
+impl LineNumbers {
+    fn new(size: usize) -> Self {
+        let mut line_numbers = Vec::with_capacity(size);
+        line_numbers.push(0);
+        LineNumbers {
+            line_numbers: RefCell::new(line_numbers),
+        }
+    }
+
+    fn get<T>(&self, i: usize, minima: &[(usize, T)]) -> usize {
+        while self.line_numbers.borrow_mut().len() < i + 1 {
+            let pos = self.line_numbers.borrow().len();
+            let line_number = 1 + self.get(minima[pos].0, minima);
+            self.line_numbers.borrow_mut().push(line_number);
+        }
+
+        self.line_numbers.borrow()[i]
+    }
+}
+
+/// Overflow error during the [`wrap_optimal_fit`] computation.
+#[derive(Debug, PartialEq, Eq)]
+pub struct OverflowError;
+
+impl std::fmt::Display for OverflowError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "wrap_optimal_fit cost computation overflowed")
+    }
+}
+
+impl std::error::Error for OverflowError {}
+
+/// Wrap abstract fragments into lines with an optimal-fit algorithm.
+///
+/// The `line_widths` slice gives the target line width for each line
+/// (the last slice element is repeated as necessary). This can be
+/// used to implement hanging indentation.
+///
+/// The fragments must already have been split into the desired
+/// widths, this function will not (and cannot) attempt to split them
+/// further when arranging them into lines.
+///
+/// # Optimal-Fit Algorithm
+///
+/// The algorithm considers all possible break points and picks the
+/// breaks which minimizes the gaps at the end of each line. More
+/// precisely, the algorithm assigns a cost or penalty to each break
+/// point, determined by `cost = gap * gap` where `gap = target_width -
+/// line_width`. Shorter lines are thus penalized more heavily since
+/// they leave behind a larger gap.
+///
+/// We can illustrate this with the text “To be, or not to be: that is
+/// the question”. We will be wrapping it in a narrow column with room
+/// for only 10 characters. The [greedy
+/// algorithm](super::wrap_first_fit) will produce these lines, each
+/// annotated with the corresponding penalty:
+///
+/// ```text
+/// "To be, or"   1² =  1
+/// "not to be:"  0² =  0
+/// "that is"     3² =  9
+/// "the"         7² = 49
+/// "question"    2² =  4
+/// ```
+///
+/// We see that line four with “the” leaves a gap of 7 columns, which
+/// gives it a penalty of 49. The sum of the penalties is 63.
+///
+/// There are 10 words, which means that there are `2_u32.pow(9)` or
+/// 512 different ways to typeset it. We can compute
+/// the sum of the penalties for each possible line break and search
+/// for the one with the lowest sum:
+///
+/// ```text
+/// "To be,"     4² = 16
+/// "or not to"  1² =  1
+/// "be: that"   2² =  4
+/// "is the"     4² = 16
+/// "question"   2² =  4
+/// ```
+///
+/// The sum of the penalties is 41, which is better than what the
+/// greedy algorithm produced.
+///
+/// Searching through all possible combinations would normally be
+/// prohibitively slow. However, it turns out that the problem can be
+/// formulated as the task of finding column minima in a cost matrix.
+/// This matrix has a special form (totally monotone) which lets us
+/// use a [linear-time algorithm called
+/// SMAWK](https://lib.rs/crates/smawk) to find the optimal break
+/// points.
+///
+/// This means that the time complexity remains O(_n_) where _n_ is
+/// the number of words. Compared to
+/// [`wrap_first_fit`](super::wrap_first_fit), this function is about
+/// 4 times slower.
+///
+/// The optimization of per-line costs over the entire paragraph is
+/// inspired by the line breaking algorithm used in TeX, as described
+/// in the 1981 article [_Breaking Paragraphs into
+/// Lines_](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf)
+/// by Knuth and Plass. The implementation here is based on [Python
+/// code by David
+/// Eppstein](https://github.com/jfinkels/PADS/blob/master/pads/wrap.py).
+///
+/// # Errors
+///
+/// In case of an overflow during the cost computation, an `Err` is
+/// returned. Overflows happens when fragments or lines have infinite
+/// widths (`f64::INFINITY`) or if the widths are so large that the
+/// gaps at the end of lines have sizes larger than `f64::MAX.sqrt()`
+/// (approximately 1e154):
+///
+/// ```
+/// use textwrap::core::Fragment;
+/// use textwrap::wrap_algorithms::{wrap_optimal_fit, OverflowError, Penalties};
+///
+/// #[derive(Debug, PartialEq)]
+/// struct Word(f64);
+///
+/// impl Fragment for Word {
+///     fn width(&self) -> f64 { self.0 }
+///     fn whitespace_width(&self) -> f64 { 1.0 }
+///     fn penalty_width(&self) -> f64 { 0.0 }
+/// }
+///
+/// // Wrapping overflows because 1e155 * 1e155 = 1e310, which is
+/// // larger than f64::MAX:
+/// assert_eq!(wrap_optimal_fit(&[Word(0.0), Word(0.0)], &[1e155], &Penalties::default()),
+///            Err(OverflowError));
+/// ```
+///
+/// When using fragment widths and line widths which fit inside an
+/// `u64`, overflows cannot happen. This means that fragments derived
+/// from a `&str` cannot cause overflows.
+///
+/// **Note:** Only available when the `smawk` Cargo feature is
+/// enabled.
+pub fn wrap_optimal_fit<'a, 'b, T: Fragment>(
+    fragments: &'a [T],
+    line_widths: &'b [f64],
+    penalties: &'b Penalties,
+) -> Result<Vec<&'a [T]>, OverflowError> {
+    // The final line width is used for all remaining lines.
+    let default_line_width = line_widths.last().copied().unwrap_or(0.0);
+    let mut widths = Vec::with_capacity(fragments.len() + 1);
+    let mut width = 0.0;
+    widths.push(width);
+    for fragment in fragments {
+        width += fragment.width() + fragment.whitespace_width();
+        widths.push(width);
+    }
+
+    let line_numbers = LineNumbers::new(fragments.len());
+
+    let minima = smawk::online_column_minima(0.0, widths.len(), |minima, i, j| {
+        // Line number for fragment `i`.
+        let line_number = line_numbers.get(i, minima);
+        let line_width = line_widths
+            .get(line_number)
+            .copied()
+            .unwrap_or(default_line_width);
+        let target_width = line_width.max(1.0);
+
+        // Compute the width of a line spanning fragments[i..j] in
+        // constant time. We need to adjust widths[j] by subtracting
+        // the whitespace of fragment[j-1] and then add the penalty.
+        let line_width = widths[j] - widths[i] - fragments[j - 1].whitespace_width()
+            + fragments[j - 1].penalty_width();
+
+        // We compute cost of the line containing fragments[i..j]. We
+        // start with values[i].1, which is the optimal cost for
+        // breaking before fragments[i].
+        //
+        // First, every extra line cost NLINE_PENALTY.
+        let mut cost = minima[i].1 + penalties.nline_penalty as f64;
+
+        // Next, we add a penalty depending on the line length.
+        if line_width > target_width {
+            // Lines that overflow get a hefty penalty.
+            let overflow = line_width - target_width;
+            cost += overflow * penalties.overflow_penalty as f64;
+        } else if j < fragments.len() {
+            // Other lines (except for the last line) get a milder
+            // penalty which depend on the size of the gap.
+            let gap = target_width - line_width;
+            cost += gap * gap;
+        } else if i + 1 == j
+            && line_width < target_width / penalties.short_last_line_fraction as f64
+        {
+            // The last line can have any size gap, but we do add a
+            // penalty if the line is very short (typically because it
+            // contains just a single word).
+            cost += penalties.short_last_line_penalty as f64;
+        }
+
+        // Finally, we discourage hyphens.
+        if fragments[j - 1].penalty_width() > 0.0 {
+            // TODO: this should use a penalty value from the fragment
+            // instead.
+            cost += penalties.hyphen_penalty as f64;
+        }
+
+        cost
+    });
+
+    for (_, cost) in &minima {
+        if cost.is_infinite() {
+            return Err(OverflowError);
+        }
+    }
+
+    let mut lines = Vec::with_capacity(line_numbers.get(fragments.len(), &minima));
+    let mut pos = fragments.len();
+    loop {
+        let prev = minima[pos].0;
+        lines.push(&fragments[prev..pos]);
+        pos = prev;
+        if pos == 0 {
+            break;
+        }
+    }
+
+    lines.reverse();
+    Ok(lines)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[derive(Debug, PartialEq)]
+    struct Word(f64);
+
+    #[rustfmt::skip]
+    impl Fragment for Word {
+        fn width(&self) -> f64 { self.0 }
+        fn whitespace_width(&self) -> f64 { 1.0 }
+        fn penalty_width(&self) -> f64 { 0.0 }
+    }
+
+    #[test]
+    fn wrap_fragments_with_infinite_widths() {
+        let words = vec![Word(f64::INFINITY)];
+        assert_eq!(
+            wrap_optimal_fit(&words, &[0.0], &Penalties::default()),
+            Err(OverflowError)
+        );
+    }
+
+    #[test]
+    fn wrap_fragments_with_huge_widths() {
+        let words = vec![Word(1e200), Word(1e250), Word(1e300)];
+        assert_eq!(
+            wrap_optimal_fit(&words, &[1e300], &Penalties::default()),
+            Err(OverflowError)
+        );
+    }
+
+    #[test]
+    fn wrap_fragments_with_large_widths() {
+        // The gaps will be of the sizes between 1e25 and 1e75. This
+        // makes the `gap * gap` cost fit comfortably in a f64.
+        let words = vec![Word(1e25), Word(1e50), Word(1e75)];
+        assert_eq!(
+            wrap_optimal_fit(&words, &[1e100], &Penalties::default()),
+            Ok(vec![&vec![Word(1e25), Word(1e50), Word(1e75)][..]])
+        );
+    }
+}
-- 
cgit v1.2.3