1# Changelog 2 3This file lists the most important changes made in each release of 4`textwrap`. 5 6## Version 0.14.2 (2021-06-27) 7 8The 0.14.1 release included more changes than intended and has been 9yanked. The change intended for 0.14.1 is now included in 0.14.2. 10 11## Version 0.14.1 (2021-06-26) 12 13This release fixes a panic reported by @Makoto, thanks! 14 15* [#391](https://github.com/mgeisler/textwrap/pull/391): Fix panic in 16 `find_words` due to string access outside of a character boundary. 17 18## Version 0.14.0 (2021-06-05) 19 20This is a major feature release which makes Textwrap more configurable 21and flexible. The high-level API of `textwrap::wrap` and 22`textwrap::fill` remains unchanged, but low-level structs have moved 23around. 24 25The biggest change is the introduction of new generic type parameters 26to the `Options` struct. These parameters lets you statically 27configure the wrapping algorithm, the word separator, and the word 28splitter. If you previously spelled out the full type for `Options`, 29you now need to take the extra type parameters into account. This 30means that 31 32```rust 33let options: Options<HyphenSplitter> = Options::new(80); 34``` 35 36changes to 37 38```rust 39let options: Options< 40 wrap_algorithms::FirstFit, 41 word_separators::AsciiSpace, 42 word_splitters::HyphenSplitter, 43> = Options::new(80); 44``` 45 46This is quite a mouthful, so we suggest using type inferrence where 47possible. You won’t see any chance if you call `wrap` directly with a 48width or with an `Options` value constructed on the fly. Please open 49an issue if this causes problems for you! 50 51### New `WordSeparator` Trait 52 53* [#332](https://github.com/mgeisler/textwrap/pull/332): Add 54 `WordSeparator` trait to allow customizing how words are found in a 55 line of text. Until now, Textwrap would always assume that words are 56 separated by ASCII space characters. You can now customize this as 57 needed. 58 59* [#313](https://github.com/mgeisler/textwrap/pull/313): Add support 60 for using the Unicode line breaking algorithm to find words. This is 61 done by adding a second implementation of the new `WordSeparator` 62 trait. The implementation uses the unicode-linebreak crate, which is 63 a new optional dependency. 64 65 With this, Textwrap can be used with East-Asian languages such as 66 Chinese or Japanese where there are no spaces between words. 67 Breaking a long sequence of emojis is another example where line 68 breaks might be wanted even if there are no whitespace to be found. 69 Feedback would be appreciated for this feature. 70 71 72### Indent 73 74* [#353](https://github.com/mgeisler/textwrap/pull/353): Trim trailing 75 whitespace from `prefix` in `indent`. 76 77 Before, empty lines would get no prefix added. Now, empty lines have 78 a trimmed prefix added. This little trick makes `indent` much more 79 useful since you can now safely indent with `"# "` without creating 80 trailing whitespace in the output due to the trailing whitespace in 81 your prefix. 82 83* [#354](https://github.com/mgeisler/textwrap/pull/354): Make `indent` 84 about 20% faster by preallocating the output string. 85 86 87### Documentation 88 89* [#308](https://github.com/mgeisler/textwrap/pull/308): Document 90 handling of leading and trailing whitespace when wrapping text. 91 92### WebAssembly Demo 93 94* [#310](https://github.com/mgeisler/textwrap/pull/310): Thanks to 95 WebAssembly, you can now try out Textwrap directly in your browser. 96 Please try it out: https://mgeisler.github.io/textwrap/. 97 98### New Generic Parameters 99 100* [#331](https://github.com/mgeisler/textwrap/pull/331): Remove outer 101 boxing from `Options`. 102 103* [#357](https://github.com/mgeisler/textwrap/pull/357): Replace 104 `core::WrapAlgorithm` enum with a `wrap_algorithms::WrapAlgorithm` 105 trait. This allows for arbitrary wrapping algorithms to be plugged 106 into the library. 107 108* [#358](https://github.com/mgeisler/textwrap/pull/358): Switch 109 wrapping functions to use a slice for `line_widths`. 110 111* [#368](https://github.com/mgeisler/textwrap/pull/368): Move 112 `WordSeparator` and `WordSplitter` traits to separate modules. 113 Before, Textwrap had several top-level structs such as 114 `NoHyphenation` and `HyphenSplitter`. These implementations of 115 `WordSplitter` now lives in a dedicated `word_splitters` module. 116 Similarly, we have a new `word_separators` module for 117 implementations of `WordSeparator`. 118 119* [#369](https://github.com/mgeisler/textwrap/pull/369): Rename 120 `Options::splitter` to `Options::word_splitter` for consistency with 121 the other fields backed by traits. 122 123## Version 0.13.4 (2021-02-23) 124 125This release removes `println!` statements which was left behind in 126`unfill` by mistake. 127 128* [#296](https://github.com/mgeisler/textwrap/pull/296): Improve house 129 building example with more comments. 130* [#297](https://github.com/mgeisler/textwrap/pull/297): Remove debug 131 prints in the new `unfill` function. 132 133## Version 0.13.3 (2021-02-20) 134 135This release contains a bugfix for `indent` and improved handling of 136emojis. We’ve also added a new function for formatting text in columns 137and functions for reformatting already wrapped text. 138 139* [#276](https://github.com/mgeisler/textwrap/pull/276): Extend 140 `core::display_width` to handle emojis when the unicode-width Cargo 141 feature is disabled. 142* [#279](https://github.com/mgeisler/textwrap/pull/279): Make `indent` 143 preserve existing newlines in the input string. Before, 144 `indent("foo", "")` would return `"foo\n"` by mistake. It now 145 returns `"foo"` instead. 146* [#281](https://github.com/mgeisler/textwrap/pull/281): Ensure all 147 `Options` fields have examples. 148* [#282](https://github.com/mgeisler/textwrap/pull/282): Add a 149 `wrap_columns` function. 150* [#294](https://github.com/mgeisler/textwrap/pull/294): Add new 151 `unfill` and `refill` functions. 152 153## Version 0.13.2 (2020-12-30) 154 155This release primarily makes all dependencies optional. This makes it 156possible to slim down textwrap as needed. 157 158* [#254](https://github.com/mgeisler/textwrap/pull/254): `impl 159 WordSplitter` for `Box<T> where T: WordSplitter`. 160* [#255](https://github.com/mgeisler/textwrap/pull/255): Use command 161 line arguments as initial text in interactive example. 162* [#256](https://github.com/mgeisler/textwrap/pull/256): Introduce 163 fuzz tests for `wrap_optimal_fit` and `wrap_first_fit`. 164* [#260](https://github.com/mgeisler/textwrap/pull/260): Make the 165 unicode-width dependency optional. 166* [#261](https://github.com/mgeisler/textwrap/pull/261): Make the 167 smawk dependency optional. 168 169## Version 0.13.1 (2020-12-10) 170 171This is a bugfix release which fixes a regression in 0.13.0. The bug 172meant that colored text was wrapped incorrectly. 173 174* [#245](https://github.com/mgeisler/textwrap/pull/245): Support 175 deleting a word with Ctrl-Backspace in the interactive demo. 176* [#246](https://github.com/mgeisler/textwrap/pull/246): Show build 177 type (debug/release) in interactive demo. 178* [#249](https://github.com/mgeisler/textwrap/pull/249): Correctly 179 compute width while skipping over ANSI escape sequences. 180 181## Version 0.13.0 (2020-12-05) 182 183This is a major release which rewrites the core logic, adds many new 184features, and fixes a couple of bugs. Most programs which use 185`textwrap` stays the same, incompatibilities and upgrade notes are 186given below. 187 188Clone the repository and run the following to explore the new features 189in an interactive demo (Linux only): 190 191```sh 192$ cargo run --example interactive --all-features 193``` 194 195### Bug Fixes 196 197#### Rewritten core wrapping algorithm 198 199* [#221](https://github.com/mgeisler/textwrap/pull/221): Reformulate 200 wrapping in terms of words with whitespace and penalties. 201 202The core wrapping algorithm has been completely rewritten. This fixed 203bugs and simplified the code, while also making it possible to use 204`textwrap` outside the context of the terminal. 205 206As part of this, trailing whitespace is now discarded consistently 207from wrapped lines. Before we would inconsistently remove whitespace 208at the end of wrapped lines, except for the last. Leading whitespace 209is still preserved. 210 211### New Features 212 213#### Optimal-fit wrapping 214 215* [#234](https://github.com/mgeisler/textwrap/pull/234): Introduce 216 wrapping using an optimal-fit algorithm. 217 218This release adds support for new wrapping algorithm which finds a 219globally optimal set of line breaks, taking certain penalties into 220account. As an example, the old algorithm would produce 221 222 "To be, or" 223 "not to be:" 224 "that is" 225 "the" 226 "question" 227 228Notice how the fourth line with “the” is very short. The new algorithm 229shortens the previous lines slightly to produce fewer short lines: 230 231 "To be," 232 "or not to" 233 "be: that" 234 "is the" 235 "question" 236 237Use the new `textwrap::core::WrapAlgorithm` enum to select between the 238new and old algorithm. By default, the new algorithm is used. 239 240The optimal-fit algorithm is inspired by the line breaking algorithm 241used in TeX, described in the 1981 article [_Breaking Paragraphs into 242Lines_](http://www.eprg.org/G53DOC/pdfs/knuth-plass-breaking.pdf) by 243Knuth and Plass. 244 245#### In-place wrapping 246 247* [#226](https://github.com/mgeisler/textwrap/pull/226): Add a 248 `fill_inplace` function. 249 250When the text you want to fill is already a temporary `String`, you 251can now mutate it in-place with `fill_inplace`: 252 253```rust 254let mut greeting = format!("Greetings {}, welcome to the game! You have {} lives left.", 255 player.name, player.lives); 256fill_inplace(&mut greeting, line_width); 257``` 258 259This is faster than calling `fill` and it will reuse the memory 260already allocated for the string. 261 262### Changed Features 263 264#### `Wrapper` is replaced with `Options` 265 266* [#213](https://github.com/mgeisler/textwrap/pull/213): Simplify API 267 with only top-level functions. 268* [#215](https://github.com/mgeisler/textwrap/pull/215): Reintroducing 269 the type parameter on `Options` (previously known as `Wrapper`). 270* [#219](https://github.com/mgeisler/textwrap/pull/219): Allow using 271 trait objects with `fill` & `wrap`. 272* [#227](https://github.com/mgeisler/textwrap/pull/227): Replace 273 `WrapOptions` with `Into<Options>`. 274 275The `Wrapper` struct held the options (line width, indentation, etc) 276for wrapping text. It was also the entry point for actually wrapping 277the text via its methods such as `wrap`, `wrap_iter`, 278`into_wrap_iter`, and `fill` methods. 279 280The struct has been replaced by a simpler `Options` struct which only 281holds options. The `Wrapper` methods are gone, their job has been 282taken over by the top-level `wrap` and `fill` functions. The signature 283of these functions have changed from 284 285```rust 286fn fill(s: &str, width: usize) -> String; 287 288fn wrap(s: &str, width: usize) -> Vec<Cow<'_, str>>; 289``` 290 291to the more general 292 293```rust 294fn fill<'a, S, Opt>(text: &str, options: Opt) -> String 295where 296 S: WordSplitter, 297 Opt: Into<Options<'a, S>>; 298 299fn wrap<'a, S, Opt>(text: &str, options: Opt) -> Vec<Cow<'_, str>> 300where 301 S: WordSplitter, 302 Opt: Into<Options<'a, S>>; 303``` 304 305The `Into<Options<'a, S>` bound allows you to pass an `usize` (which 306is interpreted as the line width) *and* a full `Options` object. This 307allows the new functions to work like the old, plus you can now fully 308customize the behavior of the wrapping via `Options` when needed. 309 310Code that call `textwrap::wrap` or `textwrap::fill` can remain 311unchanged. Code that calls into `Wrapper::wrap` or `Wrapper::fill` 312will need to be update. This is a mechanical change, please see 313[#213](https://github.com/mgeisler/textwrap/pull/213) for examples. 314 315Thanks to @CryptJar and @Koxiat for their support in the PRs above! 316 317### Removed Features 318 319* The `wrap_iter` and `into_wrap_iter` methods are gone. This means 320 that lazy iteration is no longer supported: you always get all 321 wrapped lines back as a `Vec`. This was done to simplify the code 322 and to support the optimal-fit algorithm. 323 324 The first-fit algorithm could still be implemented in an incremental 325 fashion. Please let us know if this is important to you. 326 327### Other Changes 328 329* [#206](https://github.com/mgeisler/textwrap/pull/206): Change 330 `Wrapper.splitter` from `T: WordSplitter` to `Box<dyn 331 WordSplitter>`. 332* [#216](https://github.com/mgeisler/textwrap/pull/216): Forbid the 333 use of unsafe code. 334 335## Version 0.12.1 (2020-07-03) 336 337This is a bugfix release. 338 339* Fixed [#176][issue-176]: Mention compile-time wrapping by linking to 340 the [`textwrap-macros` crate]. 341* Fixed [#193][issue-193]: Wrapping with `break_words(false)` was 342 broken and would cause extra whitespace to be inserted when words 343 were longer than the line width. 344 345## Version 0.12.0 (2020-06-26) 346 347The code has been updated to the [Rust 2018 edition][rust-2018] and 348each new release of `textwrap` will only support the latest stable 349version of Rust. Trying to support older Rust versions is a fool's 350errand: our dependencies keep releasing new patch versions that 351require newer and newer versions of Rust. 352 353The `term_size` feature has been replaced by `terminal_size`. The API 354is unchanged, it is just the name of the Cargo feature that changed. 355 356The `hyphenation` feature now only embeds the hyphenation patterns for 357US-English. This slims down the dependency. 358 359* Fixed [#140][issue-140]: Ignore ANSI escape sequences. 360* Fixed [#158][issue-158]: Unintended wrapping when using external splitter. 361* Fixed [#177][issue-177]: Update examples to the 2018 edition. 362 363## Version 0.11.0 (2018-12-09) 364 365Due to our dependencies bumping their minimum supported version of 366Rust, the minimum version of Rust we test against is now 1.22.0. 367 368* Merged [#141][issue-141]: Fix `dedent` handling of empty lines and 369 trailing newlines. Thanks @bbqsrc! 370* Fixed [#151][issue-151]: Release of version with hyphenation 0.7. 371 372## Version 0.10.0 (2018-04-28) 373 374Due to our dependencies bumping their minimum supported version of 375Rust, the minimum version of Rust we test against is now 1.17.0. 376 377* Fixed [#99][issue-99]: Word broken even though it would fit on line. 378* Fixed [#107][issue-107]: Automatic hyphenation is off by one. 379* Fixed [#122][issue-122]: Take newlines into account when wrapping. 380* Fixed [#129][issue-129]: Panic on string with em-dash. 381 382## Version 0.9.0 (2017-10-05) 383 384The dependency on `term_size` is now optional, and by default this 385feature is not enabled. This is a *breaking change* for users of 386`Wrapper::with_termwidth`. Enable the `term_size` feature to restore 387the old functionality. 388 389Added a regression test for the case where `width` is set to 390`usize::MAX`, thanks @Fraser999! All public structs now implement 391`Debug`, thanks @hcpl! 392 393* Fixed [#101][issue-101]: Make `term_size` an optional dependency. 394 395## Version 0.8.0 (2017-09-04) 396 397The `Wrapper` stuct is now generic over the type of word splitter 398being used. This means less boxing and a nicer API. The 399`Wrapper::word_splitter` method has been removed. This is a *breaking 400API change* if you used the method to change the word splitter. 401 402The `Wrapper` struct has two new methods that will wrap the input text 403lazily: `Wrapper::wrap_iter` and `Wrapper::into_wrap_iter`. Use those 404if you will be iterating over the wrapped lines one by one. 405 406* Fixed [#59][issue-59]: `wrap` could return an iterator. Thanks 407 @hcpl! 408* Fixed [#81][issue-81]: Set `html_root_url`. 409 410## Version 0.7.0 (2017-07-20) 411 412Version 0.7.0 changes the return type of `Wrapper::wrap` from 413`Vec<String>` to `Vec<Cow<'a, str>>`. This means that the output lines 414borrow data from the input string. This is a *breaking API change* if 415you relied on the exact return type of `Wrapper::wrap`. Callers of the 416`textwrap::fill` convenience function will see no breakage. 417 418The above change and other optimizations makes version 0.7.0 roughly 41915-30% faster than version 0.6.0. 420 421The `squeeze_whitespace` option has been removed since it was 422complicating the above optimization. Let us know if this option is 423important for you so we can provide a work around. 424 425* Fixed [#58][issue-58]: Add a "fast_wrap" function. 426* Fixed [#61][issue-61]: Documentation errors. 427 428## Version 0.6.0 (2017-05-22) 429 430Version 0.6.0 adds builder methods to `Wrapper` for easy one-line 431initialization and configuration: 432 433```rust 434let wrapper = Wrapper::new(60).break_words(false); 435``` 436 437It also add a new `NoHyphenation` word splitter that will never split 438words, not even at existing hyphens. 439 440* Fixed [#28][issue-28]: Support not squeezing whitespace. 441 442## Version 0.5.0 (2017-05-15) 443 444Version 0.5.0 has *breaking API changes*. However, this only affects 445code using the hyphenation feature. The feature is now optional, so 446you will first need to enable the `hyphenation` feature as described 447above. Afterwards, please change your code from 448```rust 449wrapper.corpus = Some(&corpus); 450``` 451to 452```rust 453wrapper.splitter = Box::new(corpus); 454``` 455 456Other changes include optimizations, so version 0.5.0 is roughly 45710-15% faster than version 0.4.0. 458 459* Fixed [#19][issue-19]: Add support for finding terminal size. 460* Fixed [#25][issue-25]: Handle words longer than `self.width`. 461* Fixed [#26][issue-26]: Support custom indentation. 462* Fixed [#36][issue-36]: Support building without `hyphenation`. 463* Fixed [#39][issue-39]: Respect non-breaking spaces. 464 465## Version 0.4.0 (2017-01-24) 466 467Documented complexities and tested these via `cargo bench`. 468 469* Fixed [#13][issue-13]: Immediatedly add word if it fits. 470* Fixed [#14][issue-14]: Avoid splitting on initial hyphens. 471 472## Version 0.3.0 (2017-01-07) 473 474Added support for automatic hyphenation. 475 476## Version 0.2.0 (2016-12-28) 477 478Introduced `Wrapper` struct. Added support for wrapping on hyphens. 479 480## Version 0.1.0 (2016-12-17) 481 482First public release with support for wrapping strings on whitespace. 483 484[rust-2018]: https://doc.rust-lang.org/edition-guide/rust-2018/ 485[`textwrap-macros` crate]: https://crates.io/crates/textwrap-macros 486 487[issue-13]: https://github.com/mgeisler/textwrap/issues/13 488[issue-14]: https://github.com/mgeisler/textwrap/issues/14 489[issue-19]: https://github.com/mgeisler/textwrap/issues/19 490[issue-25]: https://github.com/mgeisler/textwrap/issues/25 491[issue-26]: https://github.com/mgeisler/textwrap/issues/26 492[issue-28]: https://github.com/mgeisler/textwrap/issues/28 493[issue-36]: https://github.com/mgeisler/textwrap/issues/36 494[issue-39]: https://github.com/mgeisler/textwrap/issues/39 495[issue-58]: https://github.com/mgeisler/textwrap/issues/58 496[issue-59]: https://github.com/mgeisler/textwrap/issues/59 497[issue-61]: https://github.com/mgeisler/textwrap/issues/61 498[issue-81]: https://github.com/mgeisler/textwrap/issues/81 499[issue-99]: https://github.com/mgeisler/textwrap/issues/99 500[issue-101]: https://github.com/mgeisler/textwrap/issues/101 501[issue-107]: https://github.com/mgeisler/textwrap/issues/107 502[issue-122]: https://github.com/mgeisler/textwrap/issues/122 503[issue-129]: https://github.com/mgeisler/textwrap/issues/129 504[issue-140]: https://github.com/mgeisler/textwrap/issues/140 505[issue-141]: https://github.com/mgeisler/textwrap/issues/141 506[issue-151]: https://github.com/mgeisler/textwrap/issues/151 507[issue-158]: https://github.com/mgeisler/textwrap/issues/158 508[issue-176]: https://github.com/mgeisler/textwrap/issues/176 509[issue-177]: https://github.com/mgeisler/textwrap/issues/177 510[issue-193]: https://github.com/mgeisler/textwrap/issues/193 511