textwrap/lib.rs
1//! The textwrap library provides functions for word wrapping and
2//! indenting text.
3//!
4//! # Wrapping Text
5//!
6//! Wrapping text can be very useful in command-line programs where
7//! you want to format dynamic output nicely so it looks good in a
8//! terminal. A quick example:
9//!
10//! ```
11//! # #[cfg(feature = "smawk")] {
12//! let text = "textwrap: a small library for wrapping text.";
13//! assert_eq!(textwrap::wrap(text, 18),
14//! vec!["textwrap: a",
15//! "small library for",
16//! "wrapping text."]);
17//! # }
18//! ```
19//!
20//! The [`wrap()`] function returns the individual lines. Use
21//! [`fill()`] if you want the lines joined with `'\n'` to form a
22//! `String`.
23//!
24//! If you enable the `hyphenation` Cargo feature, you can get
25//! automatic hyphenation for a number of languages:
26//!
27//! ```
28//! #[cfg(feature = "hyphenation")] {
29//! use hyphenation::{Language, Load, Standard};
30//! use textwrap::{wrap, Options, WordSplitter};
31//!
32//! let text = "textwrap: a small library for wrapping text.";
33//! let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
34//! let options = Options::new(18).word_splitter(WordSplitter::Hyphenation(dictionary));
35//! assert_eq!(wrap(text, &options),
36//! vec!["textwrap: a small",
37//! "library for wrap-",
38//! "ping text."]);
39//! }
40//! ```
41//!
42//! See also the [`unfill()`] and [`refill()`] functions which allow
43//! you to manipulate already wrapped text.
44//!
45//! ## Wrapping Strings at Compile Time
46//!
47//! If your strings are known at compile time, please take a look at
48//! the procedural macros from the [textwrap-macros] crate.
49//!
50//! ## Displayed Width vs Byte Size
51//!
52//! To wrap text, the width of each word must be known in order to
53//! determine line breaks. By default, this library measures the width
54//! of text using the _displayed width_, not the size in bytes. The
55//! `unicode-width` Cargo feature controls this.
56//!
57//! This is important for non-ASCII text. ASCII characters such as `a`
58//! and `!` are simple and take up one column each. This means that
59//! the displayed width is equal to the string length in bytes.
60//! However, non-ASCII characters and symbols take up more than one
61//! byte when UTF-8 encoded: `é` is `0xc3 0xa9` (two bytes) and `⚙` is
62//! `0xe2 0x9a 0x99` (three bytes) in UTF-8, respectively.
63//!
64//! This is why we take care to use the displayed width instead of the
65//! byte count when computing line lengths. All functions in this
66//! library handle Unicode characters like this when the
67//! `unicode-width` Cargo feature is enabled (it is enabled by
68//! default).
69//!
70//! # Indentation and Dedentation
71//!
72//! The textwrap library also offers functions for adding a prefix to
73//! every line of a string and removing leading whitespace. As an
74//! example, [`indent()`] allows you to turn lines of text into a
75//! bullet list:
76//!
77//! ```
78//! let before = "\
79//! foo
80//! bar
81//! baz
82//! ";
83//! let after = "\
84//! * foo
85//! * bar
86//! * baz
87//! ";
88//! assert_eq!(textwrap::indent(before, "* "), after);
89//! ```
90//!
91//! Removing leading whitespace is done with [`dedent()`]:
92//!
93//! ```
94//! let before = "
95//! Some
96//! indented
97//! text
98//! ";
99//! let after = "
100//! Some
101//! indented
102//! text
103//! ";
104//! assert_eq!(textwrap::dedent(before), after);
105//! ```
106//!
107//! # Cargo Features
108//!
109//! The textwrap library can be slimmed down as needed via a number of
110//! Cargo features. This means you only pay for the features you
111//! actually use.
112//!
113//! The full dependency graph, where dashed lines indicate optional
114//! dependencies, is shown below:
115//!
116//! <img src="https://raw.githubusercontent.com/mgeisler/textwrap/main/images/textwrap-0.16.4.svg">
117//!
118//! ## Default Features
119//!
120//! These features are enabled by default:
121//!
122//! * `std`: enables standard library support. When disabled, textwrap
123//! runs in `#![no_std]` mode, requiring only the `alloc` crate.
124//!
125//! * `unicode-linebreak`: enables finding words using the
126//! [icu_segmenter] crate, which implements the line breaking
127//! algorithm described in [Unicode Standard Annex
128//! #14](https://www.unicode.org/reports/tr14/).
129//!
130//! This feature provides line breaking without support for complex
131//! scripts (Khmer, Lao, Myanmar, and Thai). See the
132//! `unicode-linebreak-complex` feature below for complex script
133//! support.
134//!
135//! This feature can be disabled if you are happy to find words
136//! separated by ASCII space characters only. People wrapping text
137//! with emojis or East Asian characters will most likely want to
138//! enable this feature. See [`WordSeparator`] for details.
139//!
140//! * `unicode-width`: enables correct width computation of non-ASCII
141//! characters via the [unicode-width] crate. Without this feature,
142//! every [`char`] is 1 column wide, except for emojis which are 2
143//! columns wide. See [`core::display_width()`] for details.
144//!
145//! This feature can be disabled if you only need to wrap ASCII
146//! text, or if the functions in [`core`] are used directly with
147//! [`core::Fragment`]s for which the widths have been computed in
148//! other ways.
149//!
150//! * `smawk`: enables linear-time wrapping of the whole paragraph via
151//! the [smawk] crate. See [`wrap_algorithms::wrap_optimal_fit()`]
152//! for details on the optimal-fit algorithm.
153//!
154//! This feature can be disabled if you only ever intend to use
155//! [`wrap_algorithms::wrap_first_fit()`].
156//!
157//! <!-- begin binary-sizes -->
158//!
159//! With Rust 1.64.0, the size impact of the above features on your
160//! binary is as follows:
161//!
162//! | Configuration | Binary Size | Delta |
163//! | :--- | ---: | ---: |
164//! | quick-and-dirty implementation | 289 KB | — KB |
165//! | textwrap without default features | 305 KB | 16 KB |
166//! | textwrap with smawk | 317 KB | 28 KB |
167//! | textwrap with unicode-width | 309 KB | 20 KB |
168//! | textwrap with unicode-linebreak | 342 KB | 53 KB |
169//!
170//! <!-- end binary-sizes -->
171//!
172//! The above sizes are the stripped sizes and the binary is compiled
173//! in release mode with this profile:
174//!
175//! ```toml
176//! [profile.release]
177//! lto = true
178//! codegen-units = 1
179//! ```
180//!
181//! See the [binary-sizes demo] if you want to reproduce these
182//! results.
183//!
184//! ## Optional Features
185//!
186//! These Cargo features enable new functionality:
187//!
188//! * `unicode-linebreak-complex`: enables finding words with support
189//! for complex scripts (Khmer, Lao, Myanmar, and Thai). It uses the
190//! [icu_segmenter] crate with full dictionary and neural network
191//! models.
192//!
193//! * `terminal_size`: enables automatic detection of the terminal
194//! width via the [terminal_size] crate. See
195//! [`Options::with_termwidth()`] for details.
196//!
197//! * `hyphenation`: enables language-sensitive hyphenation via the
198//! [hyphenation] crate. See the [`word_splitters::WordSplitter`]
199//! trait for details.
200//!
201//! [icu_segmenter]: https://docs.rs/icu_segmenter/
202//! [unicode-width]: https://docs.rs/unicode-width/
203//! [smawk]: https://docs.rs/smawk/
204//! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/main/examples/binary-sizes
205//! [textwrap-macros]: https://docs.rs/textwrap-macros/
206//! [terminal_size]: https://docs.rs/terminal_size/
207//! [hyphenation]: https://docs.rs/hyphenation/
208
209#![doc(html_root_url = "https://docs.rs/textwrap/0.16.4")]
210#![forbid(unsafe_code)] // See https://github.com/mgeisler/textwrap/issues/210
211#![deny(missing_docs)]
212#![deny(missing_debug_implementations)]
213#![cfg_attr(not(feature = "std"), no_std)]
214
215extern crate alloc;
216
217// Make `cargo test` execute the README doctests.
218#[cfg(all(doctest, feature = "std"))]
219#[doc = include_str!("../README.md")]
220mod readme_doctest {}
221
222pub mod core;
223#[cfg(fuzzing)]
224pub mod fuzzing;
225pub mod word_splitters;
226pub mod wrap_algorithms;
227
228mod columns;
229mod fill;
230mod indentation;
231mod line_ending;
232mod options;
233mod refill;
234#[cfg(feature = "terminal_size")]
235mod termwidth;
236mod word_separators;
237mod wrap;
238
239pub use columns::wrap_columns;
240pub use fill::{fill, fill_inplace};
241pub use indentation::{dedent, indent};
242pub use line_ending::LineEnding;
243pub use options::Options;
244pub use refill::{refill, unfill};
245#[cfg(feature = "terminal_size")]
246pub use termwidth::termwidth;
247pub use word_separators::WordSeparator;
248pub use word_splitters::WordSplitter;
249pub use wrap::wrap;
250pub use wrap_algorithms::WrapAlgorithm;