//! Word splitting functionality. //! //! To wrap text into lines, long words sometimes need to be split //! across lines. The [`WordSplitter`] enum defines this //! functionality.
usecrate::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)] pubenum 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 variant 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 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]); /// ``` pubfn split_points(&self, word: &str) -> Vec<usize> { matchself {
WordSplitter::NoHyphenation => Vec::new(),
WordSplitter::HyphenSplitter => { letmut 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 '-'.
}
}
/// 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. pubfn 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| { letmut prev = 0; letmut split_points = word_splitter.split_points(&word).into_iter();
std::iter::from_fn(move || { iflet 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 { usesuper::*;
// 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);
};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.