Repository: mgeisler/textwrap (language: rust)

Issue:
Support for tabs and hanging indent
The use case I'm thinking of is for a command-line program describing its options. Eg

```
-i <filename>   Input file. ELF, PE (portable executable)
                object file formats are accepted.
-o <filename>   Output file. Binary, Intel hex or Motorola
                hex file formats are accepted.
```


Discussion / hints:
Hi @cmcqueen  That's a great use case! We do have some support for this already via the `initial_indent` and `subsequent_indent` fields on [`Wrapper`](https://docs.rs/textwrap/0.12.1/textwrap/struct.Wrapper.html#method.initial_indent).

That is, you can do this kind of indentation by first constructing the `"-i <filename>   "` part, set that as `initial_indent` and then construct a string with spaces of the same length for the `subsequent_indent`, Something like this:

```rust
pub fn main() {
    let options = vec![
        (
            'i',
            "filename",
            "Input file. ELF, PE (portable executable) object file formats are accepted.",
        ),
        (
            'o',
            "filename",
            "Output file. Binary, Intel hex or Motorola hex file formats are accepted.",
        ),
        (
            'v',
            "int",
            "Verbosity level. Set to 42 to receive more output than you ever wanted.",
        ),
    ];

    for (short, param, help) in &options {
        let left_column = format!("-{} <{}>", short, param);
        // Adjust 20 as needed, perhaps compute it from the longest parameter name
        let initial_indent = format!("{:1$}", left_column, 20); 
        let subsequent_indent = " ".repeat(20);
        let wrapper = textwrap::Wrapper::new(65)  // 65 is your line width
            .initial_indent(&initial_indent)
            .subsequent_indent(&subsequent_indent);
        println!("{}", wrapper.fill(help));
    }
}
```

[Playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e9dee5c9400ad9775f8c9e2b5984193f).

If you want to be more careful, you should measure the length of the strings using the [unicode-width crate](http://docs.rs/unicode_width), just like textwrap does internally.

Does this help? By the way, crates like [clap](http://docs.rs/clap) already do this kind of formatting for you.
Having written the code above, I realize that it could be useful to ship a utility function that does this kind of thing.
Thanks, `initial_indent` and `subsequent_indent` are great (as for that Python package).

I guess they're not so easy to find in the documentation, since one doesn't find them on the main page, but has to follow the `Wrapper` struct link to find them. Whereas in the [Python `textwrap` documentation](https://docs.python.org/3/library/textwrap.html), they're described succinctly on the one documentation page.
Thanks, that's good feedback! I think I need a Features section somewhere on the first page you see in the documentation. Like this:

# Features

* Supports Unicode characters and emojis.
* Support for hanging indentation via the `initial_indent` and `subsequent_indent`.
* Automatic handling of ANSI color codes.
* more stuff as I think of it :-)

I'll keep the issue open so I remember to update the documentation like this before the next release.


Fix patch:
diff --git a/src/lib.rs b/src/lib.rs
index 6aa38770..b6e5a037 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -184,13 +184,15 @@ pub mod core;
 pub struct Options<'a, S: ?Sized = Box<dyn WordSplitter>> {
     /// The width in columns at which the text will be wrapped.
     pub width: usize,
-    /// Indentation used for the first line of output.
+    /// 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.
+    /// 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`.
+    /// `self.width`. See the [`Options::break_words`] method.
     pub break_words: bool,
     /// Wraping algorithm to use, see [`core::WrapAlgorithm`] for
     /// details.
@@ -422,10 +424,13 @@ impl<'a, S: WordSplitter> Options<'a, S> {
     /// Classic paragraph indentation can be achieved by specifying an
     /// initial indentation and wrapping each paragraph by itself:
     ///
-    /// ```no_run
-    /// use textwrap::Options;
+    /// ```
+    /// use textwrap::{Options, wrap};
     ///
-    /// let options = Options::new(15).initial_indent("    ");
+    /// 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
@@ -444,12 +449,24 @@ impl<'a, S: WordSplitter> Options<'a, S> {
     /// Combining initial and subsequent indentation lets you format a
     /// single paragraph as a bullet list:
     ///
-    /// ```no_run
-    /// use textwrap::Options;
+    /// ```
+    /// use textwrap::{Options, wrap};
     ///
-    /// let options = Options::new(15)
+    /// 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
@@ -464,6 +481,21 @@ impl<'a, S: WordSplitter> Options<'a, S> {
     /// than `self.width` can be broken, or if they will be left
     /// sticking out into the right margin.
     ///
+    /// # 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, setting: bool) -> Self {
         Options {
@@ -474,7 +506,7 @@ impl<'a, S: WordSplitter> Options<'a, S> {
 
     /// Change [`self.wrap_algorithm`].
     ///
-    /// See  [`core::WrapAlgorithm`] for details on the choices.
+    /// See [`core::WrapAlgorithm`] for details on the choices.
     ///
     /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm
     pub fn wrap_algorithm(self, wrap_algorithm: core::WrapAlgorithm) -> Self {


Test patch: