//! Word splitting functionality. //! //! To wrap text into lines, long words sometimes need to be split //! across lines. The [`WordSplitter`] trait defines this //! functionality. [`HyphenSplitter`] is the default implementation of //! this treat: it will simply split words on existing hyphens. /// The `WordSplitter` trait describes where words can be split. /// /// If the textwrap crate has been compiled with the `hyphenation` /// Cargo feature enabled, you will find an implementation of /// `WordSplitter` by the `hyphenation::Standard` struct. Use this /// struct for language-aware hyphenation: /// /// ``` /// #[cfg(feature = "hyphenation")] /// { /// use hyphenation::{Language, Load, Standard}; /// use textwrap::{wrap, Options}; /// /// let text = "Oxidation is the loss of electrons."; /// let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); /// let options = Options::new(8).splitter(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/ pub trait WordSplitter: std::fmt::Debug { /// Return all possible indices where `word` can be split. /// /// The indices returned must be in range `0..word.len()`. They /// should 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::{HyphenSplitter, NoHyphenation, WordSplitter}; /// assert_eq!(NoHyphenation.split_points("cannot-be-split"), vec![]); /// assert_eq!(HyphenSplitter.split_points("can-be-split"), vec![4, 7]); /// ``` fn split_points(&self, word: &str) -> Vec; } impl WordSplitter for Box { fn split_points(&self, word: &str) -> Vec { use std::ops::Deref; self.deref().split_points(word) } } impl WordSplitter for &T { fn split_points(&self, word: &str) -> Vec { (*self).split_points(word) } } /// Use this as a [`Options.splitter`] to avoid any kind of /// hyphenation: /// /// ``` /// use textwrap::{wrap, NoHyphenation, Options}; /// /// let options = Options::new(8).splitter(NoHyphenation); /// assert_eq!(wrap("foo bar-baz", &options), /// vec!["foo", "bar-baz"]); /// ``` /// /// [`Options.splitter`]: super::Options::splitter #[derive(Clone, Copy, Debug)] pub struct NoHyphenation; /// `NoHyphenation` implements `WordSplitter` by not splitting the /// word at all. impl WordSplitter for NoHyphenation { fn split_points(&self, _: &str) -> Vec { Vec::new() } } /// Simple and default way to split words: splitting on existing /// hyphens only. /// /// You probably don't need to use this type since it's already used /// by default by [`Options::new`](super::Options::new). #[derive(Clone, Copy, Debug)] pub struct HyphenSplitter; /// `HyphenSplitter` is the default `WordSplitter` used by /// [`Options::new`](super::Options::new). It will split words on any /// 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"`. impl WordSplitter for HyphenSplitter { fn split_points(&self, word: &str) -> Vec { 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 } } /// 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")] impl WordSplitter for hyphenation::Standard { fn split_points(&self, word: &str) -> Vec { use hyphenation::Hyphenator; self.hyphenate(word).breaks } }