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