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

Refactor #1976

Merged
merged 1 commit into from
Aug 28, 2020
Merged

Refactor #1976

merged 1 commit into from
Aug 28, 2020

Conversation

CreepySkeleton
Copy link
Contributor

@CreepySkeleton CreepySkeleton commented Jun 18, 2020

Refactor coloring/help/errors facility.

Improvements

  1. Creation of of clap::Error is now completely separate from actual emitting. It cannot fail anymore.

  2. Significant code bloat decrease:

    Before

     File  .text     Size Crate
     9.4%  56.8% 289.5KiB clap
     4.8%  28.7% 146.2KiB std
     1.9%  11.6%  59.1KiB [Unknown]
     0.2%   0.9%   4.8KiB indexmap
     0.1%   0.3%   1.6KiB textwrap
     0.0%   0.1%     307B probe
     0.0%   0.0%      41B os_str_bytes
    16.6% 100.0% 509.8KiB .text section size, the file size is 3.0MiB
    

    After

     File  .text     Size Crate
     8.6%  53.7% 262.5KiB clap
     5.0%  31.2% 152.5KiB std
     1.9%  12.1%  59.1KiB [Unknown]
     0.2%   1.0%   4.8KiB indexmap
     0.1%   0.3%   1.6KiB textwrap
     0.0%   0.1%     307B probe
     0.0%   0.0%      41B os_str_bytes
    16.1% 100.0% 489.1KiB .text section size, the file size is 3.0MiB
    
  3. No more our own unicorn reimplementations of str::split. I consider this a huge improvement.

Changes to public API

  1. App::write_[long_]version is replaced with render version. It was necessary in order to simplify some code. I found that these methods are very rarely used, and what those people really needed was String.

    Actually, in light of App::get[_long]_version, I think we could just remove it. @pksunkara Any objections?

  2. Error::cause and Error::info fields are removed. I've never seen them used in practice, and they are responsible for about 10 KiB of code bloat.

  3. print_help* and write_help* now return io::Result instead of clap::Result. There's no point to convert them to clap errors.

Performance regression

I haven't done proper benchmarking yet, but I expect certain regression in error/help rendering. I suppose the regression is negligible because printing is slow enough on it's own, but I'm open to continue working on improving it.

@pksunkara pksunkara self-requested a review June 18, 2020 18:20
Copy link
Member

@pksunkara pksunkara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only early review. I will finish the full review once my 2 comments are addressed.

src/parse/parser.rs Outdated Show resolved Hide resolved
src/parse/errors.rs Show resolved Hide resolved
@CreepySkeleton CreepySkeleton force-pushed the refactor branch 2 times, most recently from e7125a4 to 49b5cff Compare July 6, 2020 19:48
@CreepySkeleton CreepySkeleton marked this pull request as ready for review July 6, 2020 19:49
@pksunkara
Copy link
Member

Something is bugging me about this PR and I can't exactly pinpoint what it is. Which is leading me to procrastinate on reviewing this. 😞

@CreepySkeleton
Copy link
Contributor Author

ping @pksunkara

This PR is essentially a blocker for any other change to the error facility because it spans over almost thousand of lines of code over there.

Copy link
Member

@pksunkara pksunkara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error::cause and Error::info should not be removed. People should have the option to use clap for other kind of stuff too. cause should have plain error message and info should have programmable error information.

I understand the concern about bloat, but we can always hide these if terminal-only (or other appropriately named) feature flag is enabled. But let's decide on that later though separate from this PR.

@CreepySkeleton
Copy link
Contributor Author

It's not just about bloat, it's about feature creep. This is a golden opportunity to prune unused and redundant stuff, would be a real shame to miss it.

Actually, this talk was the reason I removed the fields in the first place - I saw some useless code and wanted to trigger the discussion around it in hope somebody would come with a compelling reason not to remove it. Your argument is basically "let's leave it just in case". Did you ever use them? Can you give an example (some code from a real project, not a hello-world)?

My arguments:

  • cause is useless because it's err.to_string().split("\n\n").next().unwrap(). Literally. I'm not opposed to having a method encapsulating that - which will be removed by LTO if not used by the way, but I don't see any reason in dedicating a separate field for that and even making use of std::format! which isn't exactly lightweight.

    I also don't have a tiniest idea of what it can be used for.

  • info is useless because I fail to see how it can possibly be used. I have talked to a few people and none of them used these fields and none of them could come up with an idea how-to. While there's obviously sampling bias at play, I think "nobody needs it" is good null hypothesis here.

    I'm not so sure on this one in light of imp(errors): Provide the missing required arguments as info #2016 , but that person didn't supply any particular examples either. I believe this is just a miscommunication of sorts and what they want is actually .to_string(). Let's continue the discussion there.

@pksunkara
Copy link
Member

pksunkara commented Jul 20, 2020

cause is not useless. If I want to use my CLI in 2 different places (one terminal with colors and one where I don't want the colors (ex: chatroom like described in #2016)), I don't want to run the CLI twice. I would prefer both the error messages to exist in just one run.

@CreepySkeleton
Copy link
Contributor Author

If I want to use my CLI in 2 different places (one terminal with colors and one where I don't want the colors

That piece gives me strong impression that you're mistaking cause for to_string :) How it actually works:

The Display impl doesn't do coloring. Never. This is exactly what you're supposed to use when you want no colors. Colorizer::print is what you use if you want colors (Error::exit uses just that). We'll need to introduce pub fn Error::print(stderr: bool), but I'd like to postpone it for another PR, this one is already hell of a change.

As I noticed earlier, cause is just the first sentence from the rendered error (i.e the message without "did you mean" and "hint" stuff). For what do you think you can use this? And as noted, it can be easily emulated via err.to_string().split("\n\n").next().unwrap().

I accept your reasoning for info, I'll put it back.

src/build/app/mod.rs Outdated Show resolved Hide resolved
@pickfire
Copy link
Contributor

pickfire commented Aug 9, 2020

@CreepySkeleton Since we have the luxury to change stuff. Can we enable colored help by default? It stays hidden for quite a few crates that depends on clap, I have sent pull requests to some of them and they were surprised by this feature.

@CreepySkeleton
Copy link
Contributor Author

Colored help feature = "color" will be on by default in 3.0.

@pickfire
Copy link
Contributor

pickfire commented Aug 9, 2020

Colored help feature = "color" will be on by default in 3.0.

Yes, the feature is on by default. IIRC even in 2.0 but I mean AppSettings::ColoredHelp, not the cargo feature but the clap setting to show colored help by default. Looking at clap_derive basic example, cargo run --example basic -- -h does not show any color.

By the way, I have a comment above that you probably missed.

@CreepySkeleton
Copy link
Contributor Author

@pickfire Could you please create a separate issue about coloring? For the record, I agree. The Auto, Never, Always settings set is enough.

@pickfire pickfire mentioned this pull request Aug 10, 2020
2 tasks
src/build/app/mod.rs Outdated Show resolved Hide resolved
src/build/app/mod.rs Outdated Show resolved Hide resolved
src/build/app/mod.rs Outdated Show resolved Hide resolved
src/build/app/mod.rs Outdated Show resolved Hide resolved
src/build/app/mod.rs Show resolved Hide resolved
src/output/fmt.rs Outdated Show resolved Hide resolved
src/parse/errors.rs Show resolved Hide resolved
src/output/help.rs Outdated Show resolved Hide resolved
}

pub(crate) fn value_validation(
arg: Option<&Arg>,
err: &str,
arg: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not &Arg? Why is val and err not &str?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we don't always have the Arg, like in value_of_t.

other: Option<O>,
usage: U,
other: Option<String>,
usage: String,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not &str?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because what we need here is String, not &str. This way the caller can decide how to form the string and reuse the already formed String without reallocation on our part. In fact, the caller has String most of the time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was asking because the usage parameter in all the other errors below has &str but this one was different. I just wanted to confirm that it was intentional since it's not consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was going to make the other methods to use String too (I have some ongoing work done locally), but decided to postpone it as quite irrelevant (PR is already larger than necessary). Do you want it in this PR or as a followup?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's only &str to String and such minor stuff, you can add a new commit to this PR. We will squash after I review and approve the PR.

Copy link
Member

@pksunkara pksunkara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, can you please squash the commits once the PR is ready. I don't mind if it's just one large commit.

src/output/fmt.rs Outdated Show resolved Hide resolved
Copy link
Member

@pksunkara pksunkara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You misrebased the tests/help.rs file.

@pksunkara
Copy link
Member

Your tests are failing because new stable is released. I fixed the UI tests in #2114

Copy link
Member

@pksunkara pksunkara left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please squash now?

@pksunkara
Copy link
Member

Once squashed, you can do bors r=pksunkara to merge. 👍

@CreepySkeleton
Copy link
Contributor Author

bors r=pksunkara

@bors
Copy link
Contributor

bors bot commented Aug 28, 2020

Build succeeded:

@bors bors bot merged commit e4b5407 into master Aug 28, 2020
@bors bors bot deleted the refactor branch August 28, 2020 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants