diff options
Diffstat (limited to 'third_party/rust/textwrap/src/options.rs')
-rw-r--r-- | third_party/rust/textwrap/src/options.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/third_party/rust/textwrap/src/options.rs b/third_party/rust/textwrap/src/options.rs new file mode 100644 index 0000000000..80e420d195 --- /dev/null +++ b/third_party/rust/textwrap/src/options.rs @@ -0,0 +1,300 @@ +//! Options for wrapping text. + +use crate::{LineEnding, WordSeparator, WordSplitter, WrapAlgorithm}; + +/// Holds configuration options for wrapping and filling text. +#[non_exhaustive] +#[derive(Debug, Clone)] +pub struct Options<'a> { + /// The width in columns at which the text will be wrapped. + pub width: usize, + /// Line ending used for breaking lines. + pub line_ending: LineEnding, + /// 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 + /// [`WrapAlgorithm`] trait for details. + pub wrap_algorithm: WrapAlgorithm, + /// The line breaking algorithm to use, see the [`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, + line_ending: options.line_ending, + 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. + /// + /// The other fields are given default values as follows: + /// + /// ``` + /// # use textwrap::{LineEnding, Options, WordSplitter, WordSeparator, WrapAlgorithm}; + /// # let width = 80; + /// let options = Options::new(width); + /// assert_eq!(options.line_ending, LineEnding::LF); + /// assert_eq!(options.initial_indent, ""); + /// assert_eq!(options.subsequent_indent, ""); + /// assert_eq!(options.break_words, true); + /// + /// #[cfg(feature = "unicode-linebreak")] + /// assert_eq!(options.word_separator, WordSeparator::UnicodeBreakProperties); + /// #[cfg(not(feature = "unicode-linebreak"))] + /// assert_eq!(options.word_separator, WordSeparator::AsciiSpace); + /// + /// #[cfg(feature = "smawk")] + /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::new_optimal_fit()); + /// #[cfg(not(feature = "smawk"))] + /// assert_eq!(options.wrap_algorithm, WrapAlgorithm::FirstFit); + /// + /// assert_eq!(options.word_splitter, WordSplitter::HyphenSplitter); + /// ``` + /// + /// 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, + line_ending: LineEnding::LF, + initial_indent: "", + subsequent_indent: "", + break_words: true, + word_separator: WordSeparator::new(), + wrap_algorithm: WrapAlgorithm::new(), + word_splitter: WordSplitter::HyphenSplitter, + } + } + + /// Change [`self.line_ending`]. This specifies which of the + /// supported line endings should be used to break the lines of the + /// input text. + /// + /// # Examples + /// + /// ``` + /// use textwrap::{refill, LineEnding, Options}; + /// + /// let options = Options::new(15).line_ending(LineEnding::CRLF); + /// assert_eq!(refill("This is a little example.", options), + /// "This is a\r\nlittle example."); + /// ``` + /// + /// [`self.line_ending`]: #structfield.line_ending + pub fn line_ending(self, line_ending: LineEnding) -> Self { + Options { + line_ending, + ..self + } + } + + /// Set [`self.width`] to the given value. + /// + /// [`self.width`]: #structfield.width + pub fn width(self, width: usize) -> Self { + Options { width, ..self } + } + + /// 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, initial_indent: &'a str) -> Self { + Options { + initial_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, subsequent_indent: &'a str) -> Self { + Options { + subsequent_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. + /// + /// See [`Options::word_splitter`] instead if you want to control + /// hyphenation. + /// + /// # 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, break_words: bool) -> Self { + Options { + break_words, + ..self + } + } + + /// Change [`self.word_separator`]. + /// + /// See the [`WordSeparator`] trait for details on the choices. + /// + /// [`self.word_separator`]: #structfield.word_separator + pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> { + Options { + word_separator, + ..self + } + } + + /// Change [`self.wrap_algorithm`]. + /// + /// See the [`WrapAlgorithm`] trait for details on the choices. + /// + /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm + pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> { + Options { + wrap_algorithm, + ..self + } + } + + /// Change [`self.word_splitter`]. The [`WordSplitter`] is used to + /// fit part of a word into the current line when wrapping text. + /// + /// See [`Options::break_words`] instead if you want to control the + /// handling of words longer than the line width. + /// + /// # Examples + /// + /// ``` + /// use textwrap::{wrap, Options, WordSplitter}; + /// + /// // The default is WordSplitter::HyphenSplitter. + /// let options = Options::new(5); + /// assert_eq!(wrap("foo-bar-baz", &options), + /// vec!["foo-", "bar-", "baz"]); + /// + /// // The word is now so long that break_words kick in: + /// let options = Options::new(5) + /// .word_splitter(WordSplitter::NoHyphenation); + /// assert_eq!(wrap("foo-bar-baz", &options), + /// vec!["foo-b", "ar-ba", "z"]); + /// + /// // If you want to breaks at all, disable both: + /// let options = Options::new(5) + /// .break_words(false) + /// .word_splitter(WordSplitter::NoHyphenation); + /// assert_eq!(wrap("foo-bar-baz", &options), + /// vec!["foo-bar-baz"]); + /// ``` + /// + /// [`self.word_splitter`]: #structfield.word_splitter + pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> { + Options { + word_splitter, + ..self + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[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") + ); + } +} |