1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 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")
);
}
}