Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clap mangles multiline help text #617

Closed
vks opened this issue Aug 12, 2016 · 15 comments
Closed

Clap mangles multiline help text #617

vks opened this issue Aug 12, 2016 · 15 comments
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations
Milestone

Comments

@vks
Copy link
Contributor

vks commented Aug 12, 2016

Consider this example:

extern crate clap;

use clap::*;

static TYPE_HELP: &'static str =
"x, max, maximum   20 characters, contains symbols.{n}\
l, long           Copy-friendly, 14 characters, contains symbols.{n}\
m, med, medium    Copy-friendly, 8 characters, contains symbols.{n}\
b, basic          8 characters, no symbols.{n}\
s, short          Copy-friendly, 4 characters, no symbols.{n}\
i, pin            4 numbers.{n}\
n, name           9 letter name.{n}\
p, phrase         20 character sentence.";

fn main() {
    let matches = App::new("")
        .arg(Arg::with_name("type")
             .help(TYPE_HELP)
             .next_line_help(true))
        .get_matches();
}

The following help will be printed:

        x, max, maximum   20 characters, contains symbols.
        l, long          
        Copy-friendly, 14 characters, contains symbols.
        m, med, medium    Copy-friendly,
        8 characters, contains symbols.
        b, basic          8 characters, no symbols.
        s,
        short          Copy-friendly, 4 characters, no symbols.
        i, pin            4
        numbers.
        n, name           9 letter name.
        p, phrase         20 character
        sentence.

It seems like clap randomly inserts newlines. Increasing the terminal size does not change anything.

@kbknapp
Copy link
Member

kbknapp commented Aug 20, 2016

For your case, I believe using the default \n will work. As the the {n} is only used when you want to be able to re-align long help text, but that's not what you're doing. Try:

static TYPE_HELP: &'static str =
"x, max, maximum   20 characters, contains symbols.\n\
l, long           Copy-friendly, 14 characters, contains symbols.\n\
m, med, medium    Copy-friendly, 8 characters, contains symbols.\n\
b, basic          8 characters, no symbols.\n\
s, short          Copy-friendly, 4 characters, no symbols.\n\
i, pin            4 numbers.\n\
n, name           9 letter name.\n\
p, phrase         20 character sentence.";

@kbknapp kbknapp added T: RFC / question A-completion Area: completion generator and removed A-completion Area: completion generator labels Aug 20, 2016
@vks
Copy link
Contributor Author

vks commented Aug 21, 2016

This looks a bit better, however there are still strange newlines:

        x, max, maximum   20 characters, contains symbols.
l, long           Copy-friendly, 14 characters, contains symbols.
m, med,
        medium    Copy-friendly, 8 characters, contains symbols.
b, basic          8 characters, no symbols.
s, short         
        Copy-friendly, 4 characters, no symbols.
i, pin            4 numbers.
n, name           9 letter name.
p, phrase         20
        character sentence.

@kbknapp
Copy link
Member

kbknapp commented Aug 26, 2016

I think this is a product of the 120 default term width. Try setting set_term_width(0) on your App instance and see if that fixes it.

@vks
Copy link
Contributor Author

vks commented Aug 29, 2016

I get an 'attempt to subtract with overflow' panic at clap-2.10.2/src/app/help.rs:446 if I do that.

How can it be related to the default term width? The lines are much shorter than that. Does clap ignore \n when breaking lines?

Edit: Updating to clap 2.11 fixes the panic and does indeed work. (However, some of the autogenerated text is too long.) The line wraps still don't make sense to me if the term width is not set to 0.

@kbknapp
Copy link
Member

kbknapp commented Sep 5, 2016

@vks can you post what it looks like when not set to 0? If there are hard newlines in the help text, clap counts those simply as bytes, and not as newlines which restart the count until the next wrap. This can cause there to be two newlines, the hard one and the one that clap counts and inserts at.

@vks
Copy link
Contributor Author

vks commented Sep 6, 2016

@kbknapp Now with the new clap version and default term width I'm getting

            x, max, maximum   20
            characters, contains symbols.
            l, long           Copy-friendly,
            14 characters, contains symbols.
            m, med, medium   
            Copy-friendly, 8 characters, contains symbols.
            b, basic       
              8 characters, no symbols.
            s, short          Copy-friendly, 4
            characters, no symbols.
            i, pin            4 numbers.
            n, name
                      9 letter name.
            p, phrase         20 character sentence.

with {n}\ and

x, max, maximum   20 characters,
            contains symbols.
l, long           Copy-friendly, 14 characters,
            contains symbols.
m, med, medium    Copy-friendly, 8 characters,
            contains symbols.
b, basic          8 characters, no symbols.
s,
            short          Copy-friendly, 4 characters, no symbols.
i, pin   
                    4 numbers.
n, name           9 letter name.
p, phrase     
               20 character sentence.

with \n\.

If there are hard newlines in the help text, clap counts those simply as bytes, and not as newlines which restart the count until the next wrap.

Why can't clap just look for newlines and treat them accordingly?

@kbknapp
Copy link
Member

kbknapp commented Sep 6, 2016

@vks when I run the example you posted and add set_term_width(0) this what I see:

kevin@karch: ~/Projects/fake-rs 
➜ ./target/debug/fake-rs -h                                     ✓ [git: master]


USAGE:
    fake-rs [type]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <type>
            x, max, maximum   20 characters, contains symbols.
            l, long           Copy-friendly, 14 characters, contains symbols.
            m, med, medium    Copy-friendly, 8 characters, contains symbols.
            b, basic          8 characters, no symbols.
            s, short          Copy-friendly, 4 characters, no symbols.
            i, pin            4 numbers.
            n, name           9 letter name.
            p, phrase         20 character sentence.

@kbknapp
Copy link
Member

kbknapp commented Sep 6, 2016

Why can't clap just look for newlines and treat them accordingly?

They get printed accordingly, but clap doesn't reset the count until it inserts the next newline. This may be possible to add, but I haven't tried it yet.

@vks
Copy link
Contributor Author

vks commented Sep 6, 2016

@kbknapp Setting the term width to 0 works for me as well, as mentioned in my previous comment. This is a possible workaround, but you lose wrapping. You can just limit the help text to 80 characters and it should look fine on most terminal sizes. However, you have no control over wrapping for example the list of allowed values, which is generated by clap. You can disable its generation and create the list manually though.

Only the automatic wrapping of multiline help text is completely broken for me.

@kbknapp
Copy link
Member

kbknapp commented Sep 6, 2016

@vks I understand. I'm working the issue as we speak, since this is also an issue for rustup 😄

@kbknapp
Copy link
Member

kbknapp commented Sep 7, 2016

@vks Ok, so I've got it to where upon having enough space (i.e. term width is big enough) it will simply ignore the new lines:

USAGE:
    fake-rs [type]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <type>
            x, max, maximum   20 characters, contains symbols.
            l, long           Copy-friendly, 14 characters, contains symbols.
            m, med, medium    Copy-friendly, 8 characters, contains symbols.
            b, basic          8 characters, no symbols.
            s, short          Copy-friendly, 4 characters, no symbols.
            i, pin            4 numbers.
            n, name           9 letter name.
            p, phrase         20 character sentence.

But if the term width is smaller, it will wrap.

USAGE:
    fake-rs [type]

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <type>
            x, max, maximum   20 characters, contains symbols.
            l, long           Copy-friendly, 14 characters, contains
            symbols.
            m, med, medium    Copy-friendly, 8 characters, 
            contains symbols.
            b, basic          8 characters, no symbols.
            s, short          Copy-friendly, 4 characters, no symbols.
            i, pin            4 numbers.
            n, name           9 letter name.
            p, phrase         20 character sentence.

@kbknapp kbknapp added C-enhancement Category: Raise on the bar on expectations A-help Area: documentation, including docs.rs, readme, examples, etc... D: intermediate and removed T: RFC / question labels Sep 7, 2016
@kbknapp kbknapp added this to the 2.11.3 milestone Sep 7, 2016
@vks
Copy link
Contributor Author

vks commented Sep 7, 2016

@kbknapp Looks great! This is exactly what I was imagining.

@kbknapp
Copy link
Member

kbknapp commented Sep 7, 2016

Just put in the PR, see #650

@kbknapp
Copy link
Member

kbknapp commented Sep 7, 2016

Note, this also removes the requirement to use {n} to insert a newline, you can use \n now. Although for backwards compatibility the {n} still works fine.

@vks
Copy link
Contributor Author

vks commented Sep 7, 2016

Thanks!

mgeisler added a commit to mgeisler/clap-rs that referenced this issue Jan 31, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue Feb 5, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue Feb 8, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue May 16, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue May 16, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/textwrap that referenced this issue May 18, 2017
With this option, whitespace between words can be significant. That
is, whitespace added for alignment purposes can be kept in the output.
This means that wrapping strings like

  -v:        be very verbose and output lots of messages
  --foo:     inject extra foos in the output
  --foobar:  enable the foobar optimization (if possible)

can end up looking like

  -v:        be very verbose and
  output lots of messages
  --foo:     inject extra foos
  in the output
  --foobar:  enable the foobar
  optimization (if possible)

Fixes #28. Related to clap-rs/clap#617.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue May 22, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue May 23, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
mgeisler added a commit to mgeisler/clap-rs that referenced this issue May 29, 2017
This adds a test for the issue clap-rs#617 about keeping whitespace intact in
manually aligned text.
homu added a commit that referenced this issue May 29, 2017
Use my textwrap crate for wrapping help texts

This PR replaces the `wrap_help` function with a call to my [textwrap][1] crate which does word wrapping using a simpler and faster algorithm. I've extended the `05_ripgrep` benchmarks with a benchmark that tests generating the help text -- I figured that ripgrep would be a good example since it has lots of long strings in the help output. Comparing before and after looks like this on my machine:

```
 name              before ns/iter  after ns/iter  diff ns/iter   diff %  speedup
 build_app_long    22,101          21,099               -1,002   -4.53%   x 1.05
 build_app_short   22,138          21,205                 -933   -4.21%   x 1.04
 build_help_long   514,265         284,467            -229,798  -44.68%   x 1.81
 build_help_short  85,720          85,693                  -27   -0.03%   x 1.00
 parse_clean       23,471          22,859                 -612   -2.61%   x 1.03
 parse_complex     29,535          28,919                 -616   -2.09%   x 1.02
 parse_lots        422,815         414,577              -8,238   -1.95%   x 1.02
```

I've also added a test for #617 to ensure that the output described there is kept unchanged. That is, textwrap will now by default keep horizontal whitespace when wrapping text and this allows you to do rudimentary formatting in the help text by manually inserting whitespace.

[1]: https://crates.io/crates/textwrap

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/845)
<!-- Reviewable:end -->
kbknapp pushed a commit that referenced this issue May 29, 2017
This adds a test for the issue #617 about keeping whitespace intact in
manually aligned text.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-help Area: documentation, including docs.rs, readme, examples, etc... C-enhancement Category: Raise on the bar on expectations
Projects
None yet
Development

No branches or pull requests

2 participants