Lines Matching +full:lines +full:- +full:and +full:- +full:columns
1 //! The textwrap library provides functions for word wrapping and
6 //! Wrapping text can be very useful in command-line programs where
20 //! The [`wrap`] function returns the individual lines, use [`fill`]
21 //! is you want the lines joined with `'\n'` to form a `String`.
36 //! "library for wrap-",
41 //! See also the [`unfill`] and [`refill`] functions which allow you to
47 //! the procedural macros from the [textwrap-macros] crate.
52 //! know when to break lines. This library will by default measure the
54 //! The `unicode-width` Cargo feature controls this.
56 //! This is important for non-ASCII text. ASCII characters such as `a`
57 //! and `!` are simple and take up one column each. This means that
59 //! However, non-ASCII characters and symbols take up more than one
60 //! byte when UTF-8 encoded: `é` is `0xc3 0xa9` (two bytes) and `⚙` is
61 //! `0xe2 0x9a 0x99` (three bytes) in UTF-8, respectively.
66 //! `unicode-width` Cargo feature is enabled (it is enabled by
69 //! # Indentation and Dedentation
72 //! every line of a string and to remove leading whitespace. As an
73 //! example, the [`indent`] function allows you to turn lines of text
112 //! The full dependency graph, where dashed lines indicate optional
115 //! <img src="https://raw.githubusercontent.com/mgeisler/textwrap/master/images/textwrap-0.16.0.svg…
121 //! * `unicode-linebreak`: enables finding words using the
122 //! [unicode-linebreak] crate, which implements the line breaking
128 //! with emojis or East-Asian characters will want most likely want
131 //! * `unicode-width`: enables correct width computation of non-ASCII
132 //! characters via the [unicode-width] crate. Without this feature,
134 //! columns wide. See the [`core::display_width`] function for
142 //! * `smawk`: enables linear-time wrapping of the whole paragraph via
144 //! function for details on the optimal-fit algorithm.
149 //! <!-- begin binary-sizes -->
155 //! | :--- | ---: | ---: |
156 //! | quick-and-dirty implementation | 289 KB | — KB |
159 //! | textwrap with unicode-width | 309 KB | 20 KB |
160 //! | textwrap with unicode-linebreak | 342 KB | 53 KB |
162 //! <!-- end binary-sizes -->
164 //! The above sizes are the stripped sizes and the binary is compiled
170 //! codegen-units = 1
173 //! See the [binary-sizes demo] if you want to reproduce these
184 //! * `hyphenation`: enables language-sensitive hyphenation via the
188 //! [unicode-linebreak]: https://docs.rs/unicode-linebreak/
189 //! [unicode-width]: https://docs.rs/unicode-width/
191 //! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/master/examples/binary-sizes
192 //! [textwrap-macros]: https://docs.rs/textwrap-macros/
231 /// Holds configuration options for wrapping and filling text.
235 /// The width in columns at which the text will be wrapped.
237 /// Line ending used for breaking lines.
242 /// Indentation used for subsequent lines of output. See the
246 /// When set to `false`, some lines may be longer than
253 /// [`word_separators::WordSeparator`] trait for an overview and
258 /// language-aware machine hyphenation.
263 fn from(options: &'a Options<'a>) -> Self { in from()
278 fn from(width: usize) -> Self { in from()
297 /// #[cfg(feature = "unicode-linebreak")]
299 /// #[cfg(not(feature = "unicode-linebreak"))]
310 /// Note that the default word separator and wrap algorithms
313 pub const fn new(width: usize) -> Self { in new()
328 /// (typically because the standard input and output is not
344 pub fn with_termwidth() -> Self { in with_termwidth()
349 /// supported line endings should be used to break the lines of the
363 pub fn line_ending(self, line_ending: LineEnding) -> Self { in line_ending()
376 /// initial indentation and wrapping each paragraph by itself:
388 pub fn initial_indent(self, indent: &'a str) -> Self { in initial_indent()
396 /// is used on lines following the first line of output.
400 /// Combining initial and subsequent indentation lets you format a
424 pub fn subsequent_indent(self, indent: &'a str) -> Self { in subsequent_indent()
454 pub fn break_words(self, setting: bool) -> Self { in break_words()
466 pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> { in word_separator()
485 pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> { in wrap_algorithm()
512 /// assert_eq!(wrap("foo-bar-baz", &options),
513 /// vec!["foo-", "bar-", "baz"]);
518 /// assert_eq!(wrap("foo-bar-baz", &options),
519 /// vec!["foo-b", "ar-ba", "z"]);
525 /// assert_eq!(wrap("foo-bar-baz", &options),
526 /// vec!["foo-bar-baz"]);
530 pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> { in word_splitter()
553 /// with a two column margin to the left and the right:
558 /// let width = termwidth() - 4; // Two columns on each side.
567 pub fn termwidth() -> usize { in termwidth()
575 /// individual lines.
596 /// .initial_indent("- ")
600 /// "- Memory safety\n without\n garbage\n collection."
603 pub fn fill<'a, Opt>(text: &str, width_or_options: Opt) -> String in fill()
619 fn fill_slow_path(text: &str, options: Options<'_>) -> String { in fill_slow_path()
635 /// Unpack a paragraph of already-wrapped text.
653 /// In addition, it will recognize a common prefix and a common line
654 /// ending among the lines.
657 /// [`Options::initial_indent`] and the prefix (if any) of the the
658 /// other lines is returned in [`Options::subsequent_indent`].
665 /// for unordered lists (`'-'`, `'+'`, and `'*'`) and block quotes
667 /// comments (`'#'` and `'/'`).
670 /// that there can be no empty lines (`"\n\n"` or `"\r\n\r\n"`) within
690 pub fn unfill(text: &str) -> (String, Options<'_>) { in unfill()
691 let prefix_chars: &[_] = &[' ', '-', '+', '*', '>', '#', '/']; in unfill()
694 for (idx, line) in text.lines().enumerate() { in unfill()
697 let prefix = &line[..line.len() - without_prefix.len()]; in unfill()
750 /// The `new_width_or_options` argument specify the new width and can
752 /// [`Options::initial_indent`] and [`Options::subsequent_indent`],
789 /// - This is my
794 /// - This is my list
798 pub fn refill<'a, Opt>(filled_text: &str, new_width_or_options: Opt) -> String in refill()
821 /// The result is a vector of lines, each line is of type [`Cow<'_,
823 /// `&str` if possible. The lines do not have trailing whitespace,
833 /// let lines = wrap("Memory safety without garbage collection.", 15);
834 /// assert_eq!(lines, &[
848 /// .initial_indent("- ")
850 /// let lines = wrap("Memory safety without garbage collection.", &options);
851 /// assert_eq!(lines, &[
852 /// "- Memory safety",
859 /// # Optimal-Fit Wrapping
862 /// finding breaks which avoid short lines. We call this an
863 /// “optimal-fit algorithm” since the line breaks are computed by
865 /// “first-fit algorithm” which simply accumulates words until they no
868 /// As an example, using the first-fit algorithm to wrap the famous
875 /// # let lines = wrap("To be, or not to be: that is the question",
877 /// # assert_eq!(lines.join("\n") + "\n", "\
887 /// “question” was too large to fit? The greedy first-fit algorithm
891 /// With the optimal-fit wrapping algorithm, the previous lines are
899 /// # let lines = wrap(
903 /// # assert_eq!(lines.join("\n") + "\n", "\
916 /// The returned iterator yields lines of type `Cow<'_, str>`. If
917 /// possible, the wrapped lines will borrow from the input string. As
919 /// the input, but the subsequent lines become owned strings:
926 /// let lines = wrap("Wrapping text all day long.", &options);
927 /// let annotated = lines
944 /// ## Leading and Trailing Whitespace
946 /// As a rule, leading whitespace (indentation) is preserved and
949 /// In more details, when wrapping words into lines, words are found
954 /// "Foo␣␣␣bar␣baz" -> ["Foo␣␣␣", "bar␣", "baz"]
957 /// These words are then put into lines. The interword whitespace is
958 /// preserved, unless the lines are wrapped so that the `"Foo␣␣␣"`
969 /// first example, `"bar␣"` becomes `"bar"` and in the second case
977 /// "␣␣foo␣bar" -> ["␣␣", "foo␣", "bar"]
980 /// When put into lines, the indentation is preserved if `"foo"` fits
989 pub fn wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec<Cow<'_, str>> in wrap()
996 let mut lines = Vec::new(); in wrap() localVariable
998 wrap_single_line(line, &options, &mut lines); in wrap()
1001 lines in wrap()
1004 fn wrap_single_line<'a>(line: &'a str, options: &Options<'_>, lines: &mut Vec<Cow<'a, str>>) { in wrap_single_line()
1005 let indent = if lines.is_empty() { in wrap_single_line()
1011 lines.push(Cow::from(line.trim_end_matches(' '))); in wrap_single_line()
1013 wrap_single_line_slow_path(line, options, lines) in wrap_single_line()
1023 lines: &mut Vec<Cow<'a, str>>, in wrap_single_line_slow_path()
1042 // zero-width word fixed this. in wrap_single_line_slow_path()
1056 lines.push(Cow::from("")); in wrap_single_line_slow_path()
1069 - last_word.whitespace.len(); in wrap_single_line_slow_path()
1073 let mut result = if lines.is_empty() && !options.initial_indent.is_empty() { in wrap_single_line_slow_path()
1075 } else if !lines.is_empty() && !options.subsequent_indent.is_empty() { in wrap_single_line_slow_path()
1090 lines.push(result); in wrap_single_line_slow_path()
1093 // `last_word.whitespace` -- even if we had a penalty, we need in wrap_single_line_slow_path()
1099 /// Wrap text into columns with a given total width.
1101 /// The `left_gap`, `middle_gap` and `right_gap` arguments specify the
1102 /// strings to insert before, between, and after the columns. The
1103 /// total width of all columns and all gaps is specified using the
1108 /// If the columns are narrow, it is recommended to set
1112 /// The per-column width is computed like this:
1116 /// # let columns = 2;
1119 /// - textwrap::core::display_width(left_gap)
1120 /// - textwrap::core::display_width(right_gap)
1121 /// - textwrap::core::display_width(middle_gap) * (columns - 1);
1122 /// let column_width = inner_width / columns;
1125 /// The `text` is wrapped using [`wrap`] and the given `options`
1131 /// Panics if `columns` is zero.
1139 /// This is an example text, which is wrapped into three columns. \
1145 /// "| an example | columns. | shorter than |",
1153 /// "| example text, | columns. | shorter than |",
1158 columns: usize, in wrap_columns()
1163 ) -> Vec<String> in wrap_columns()
1167 assert!(columns > 0); in wrap_columns()
1175 .saturating_sub(core::display_width(middle_gap) * (columns - 1)); in wrap_columns()
1177 let column_width = std::cmp::max(inner_width / columns, 1); in wrap_columns()
1182 wrapped_lines.len() / columns + usize::from(wrapped_lines.len() % columns > 0); in wrap_columns()
1183 let mut lines = Vec::new(); in wrap_columns() localVariable
1186 for column_no in 0..columns { in wrap_columns()
1190 line.push_str(&" ".repeat(column_width - core::display_width(column_line))); in wrap_columns()
1196 if column_no == columns - 1 { in wrap_columns()
1203 lines.push(line); in wrap_columns()
1206 lines in wrap_columns()
1209 /// Fill `text` in-place without reallocating the input string.
1236 /// is the fastest algorithm — and the main reason to use
1241 /// leave trailing whitespace on lines. This is because we wrap by
1271 for words in &wrapped_words[..wrapped_words.len() - 1] { in fill_inplace()
1278 // We've advanced past all ' ' characters -- want to move in fill_inplace()
1279 // one ' ' backwards and insert our '\n' there. in fill_inplace()
1280 indices.push(line_offset - 1); in fill_inplace()
1312 opt_usize.word_splitter.split_points("hello-world"), in options_agree_with_usize()
1313 opt_options.word_splitter.split_points("hello-world") in options_agree_with_usize()
1379 // gets too long and is broken, the first word starts in in trailing_whitespace()
1380 // column zero and is not indented. in trailing_whitespace()
1386 // We did not reset the in_whitespace flag correctly and did in issue_99()
1387 // not handle single-character words after a line break. in issue_99()
1396 // The dash is an em-dash which takes up four bytes. We used in issue_129()
1414 // unicode-linebreak feature is enabled. in wide_character_handling()
1415 #[cfg(feature = "unicode-linebreak")] in wide_character_handling()
1427 // Previously, indentation was not applied to empty lines. in empty_line_is_indented()
1428 // However, this is somewhat inconsistent and undesirable if in empty_line_is_indented()
1430 // want to apply to all lines, empty or not. in empty_line_is_indented()
1482 // This is a corner-case showing how the long word is broken in initial_indent_break_words()
1483 // according to the width of the subsequent lines. The first in initial_indent_break_words()
1486 let options = Options::new(5).initial_indent("-->"); in initial_indent_break_words()
1487 assert_eq!(wrap("foobarbaz", &options), vec!["-->", "fooba", "rbaz"]); in initial_indent_break_words()
1492 assert_eq!(wrap("foo-bar", 5), vec!["foo-", "bar"]); in hyphens()
1498 assert_eq!(wrap("foobar-", &options), vec!["foobar-"]); in trailing_hyphen()
1503 assert_eq!(wrap("foo-bar-baz", 5), vec!["foo-", "bar-", "baz"]); in multiple_hyphens()
1510 wrap("The --foo-bar flag.", &options), in hyphens_flag()
1511 vec!["The", "--foo-", "bar", "flag."] in hyphens_flag()
1518 assert_eq!(wrap("foo--bar", &options), vec!["foo--bar"]); in repeated_hyphens()
1523 assert_eq!(wrap("Na2-CH4", 5), vec!["Na2-", "CH4"]); in hyphens_alphanumeric()
1529 assert_eq!(wrap("foo(-)bar", &options), vec!["foo(-)bar"]); in hyphens_non_alphanumeric()
1534 assert_eq!(wrap("foo-bar-baz", 9), vec!["foo-bar-", "baz"]); in multiple_splits()
1540 assert_eq!(wrap("foobar-baz", &options), vec!["foobar-", "baz"]); in forced_split()
1566 assert_eq!(wrap("foo bar-baz", &options), vec!["foo bar-", "baz"]); in simple_hyphens()
1572 assert_eq!(wrap("foo bar-baz", &options), vec!["foo", "bar-baz"]); in no_hyphenation()
1588 vec!["Interna-", "tionaliza-", "tion"] in auto_hyphenation_double_hyphenation()
1605 vec!["partici-", "pation is", "the key to", "success"] in auto_hyphenation_issue_158()
1618 vec!["garbage col-", "lection"] in split_len_hyphenation()
1625 // Lines that end with an extra hyphen are owned, the final in borrowed_lines()
1630 let lines = wrap("Internationalization", &options); in borrowed_lines() localVariable
1631 assert_eq!(lines, vec!["Interna-", "tionaliza-", "tion"]); in borrowed_lines()
1632 if let Borrowed(s) = lines[0] { in borrowed_lines()
1635 if let Borrowed(s) = lines[1] { in borrowed_lines()
1638 if let Owned(ref s) = lines[2] { in borrowed_lines()
1649 wrap("over-caffinated", &options), in auto_hyphenation_with_hyphen()
1650 vec!["over-", "caffinated"] in auto_hyphenation_with_hyphen()
1655 wrap("over-caffinated", &options), in auto_hyphenation_with_hyphen()
1656 vec!["over-", "caffi-", "nated"] in auto_hyphenation_with_hyphen()
1778 let mut text = String::from("Some text to wrap over multiple lines"); in fill_inplace_multiple_lines()
1792 let mut text = String::from("A well-chosen example"); in fill_inplace_no_hyphen_splitting()
1794 assert_eq!(text, "A\nwell-chosen\nexample"); in fill_inplace_no_hyphen_splitting()
1936 // chars *and* the second line is shorter than the first line. in unfill_only_prefixes_issue_466()
2000 // The gaps take up a total of 5 columns, so the columns are in wrap_columns_uneven_columns()
2001 // (21 - 5)/4 = 4 columns wide: in wrap_columns_uneven_columns()
2012 // Finally, when the width is 25, the columns can be resized in wrap_columns_uneven_columns()
2013 // to a width of (25 - 5)/4 = 5 columns: in wrap_columns_uneven_columns()
2021 #[cfg(feature = "unicode-width")]
2025 "Words and a few emojis wrapped in ⓶ columns", in wrap_columns_with_emojis()
2034 "✨ and a few ⚽ ⓶ columns ", in wrap_columns_with_emojis()
2045 wrap_columns("xyz", 2, 10, "----> ", " !!! ", " <----"), in wrap_columns_big_gaps()
2047 "----> x !!! z <----", // in wrap_columns_big_gaps()
2048 "----> y !!! <----" in wrap_columns_big_gaps()