Skip to content

Commit c2d26f3

Browse files
committed
Auto merge of #12578 - epage:help, r=weihanglo
feat(help): Add styling to help output ### What does this PR try to resolve? Try to make `--help` output easier to parse by using terminal styling Screenshots: ![Screenshot from 2023-09-06 09-57-11](https://github.com/rust-lang/cargo/assets/60961/61069af4-ef05-40ad-9240-fedea44d4c71) ![Screenshot from 2023-09-06 09-57-21](https://github.com/rust-lang/cargo/assets/60961/d2e69024-42aa-47c0-ad0f-24e43551b8db) ![Screenshot from 2023-09-06 09-57-36](https://github.com/rust-lang/cargo/assets/60961/e3d895e2-745f-48c6-9e84-d6fb67198d6d) *(`nargo` is my shell script wrapping `cargo run --manifest-path cargo/Cargo.toml`)* ### How should we test and review this PR? At this time, the only styling snapshotting library I know of is a pain to use, so testing this requires manually running the commands which I did. Screenshots are included for easier evaluation of the general idea. Snapshotting of the plain text output ensures we don't have accidental formatting regressions from this change since the formatting isn't as obvious from looking at the code. ### Additional information Traditionally, cargo has disabled clap's styled output. My assumed reason is that cargo mixes custom help output with auto-generated and you couldn't previously make it all styled. Clap 4.2 allowed users to pass in strings styled using ANSI escape codes, allowing us to pass in styled text that matches clap, unblocking this. In clap 4.4.1, clap gained the ability for the user to override the style. In this PR, I decided to use the new 4.4.1 feature to style clap's output to match the rest of cargo's output. Alternatively, we could use a more subdue style that clap uses by default. I used the `color-print` crate to allow something almost html-like for styling `&static str`. Alternatively, we could directly embed the ANSI escape codes harder to get write, harder to inspect), or we could do the styling at runtime and enable the `string` feature in clap. I decided to *not* style `Arg::help` messages because - It might be distracting to have the descriptions lit up like a christmas tree - It is a lot more work The one exception I made was for `--list` since it is for a psuedo-command (`...`) and I wanted to intentionally draw attention to it. #12593 made styling of `cargo -h` cleaner imo. #12592 and #12594 were improvements I noticed while doing this.
2 parents 2fc85d1 + dbbc5dd commit c2d26f3

39 files changed

+176
-74
lines changed

Cargo.lock

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ cargo-test-support = { path = "crates/cargo-test-support" }
3030
cargo-util = { version = "0.2.6", path = "crates/cargo-util" }
3131
cargo_metadata = "0.17.0"
3232
clap = "4.4.2"
33+
color-print = "0.3.4"
3334
core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] }
3435
crates-io = { version = "0.39.0", path = "crates/crates-io" }
3536
criterion = { version = "0.5.1", features = ["html_reports"] }
@@ -129,6 +130,7 @@ cargo-credential-libsecret.workspace = true
129130
cargo-credential-macos-keychain.workspace = true
130131
cargo-credential-wincred.workspace = true
131132
cargo-util.workspace = true
133+
color-print.workspace = true
132134
clap = { workspace = true, features = ["wrap_help"] }
133135
crates-io.workspace = true
134136
curl = { workspace = true, features = ["http2"] }

src/bin/cargo/cli.rs

+40-29
Original file line numberDiff line numberDiff line change
@@ -513,53 +513,64 @@ impl GlobalArgs {
513513

514514
pub fn cli() -> Command {
515515
let usage = if is_rustup() {
516-
"cargo [+toolchain] [OPTIONS] [COMMAND]\n cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..."
516+
color_print::cstr!("<cyan,bold>cargo</> <cyan>[+toolchain] [OPTIONS] [COMMAND]</>\n <cyan,bold>cargo</> <cyan>[+toolchain] [OPTIONS]</> <cyan,bold>-Zscript</> <cyan><<MANIFEST_RS>> [ARGS]...</>")
517517
} else {
518-
"cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..."
518+
color_print::cstr!("<cyan,bold>cargo</> <cyan>[OPTIONS] [COMMAND]</>\n <cyan,bold>cargo</> <cyan>[OPTIONS]</> <cyan,bold>-Zscript</> <cyan><<MANIFEST_RS>> [ARGS]...</>")
519519
};
520+
521+
let styles = {
522+
use clap::builder::styling::*;
523+
Styles::styled()
524+
.header(AnsiColor::Green.on_default() | Effects::BOLD)
525+
.usage(AnsiColor::Green.on_default() | Effects::BOLD)
526+
.literal(AnsiColor::Cyan.on_default() | Effects::BOLD)
527+
.placeholder(AnsiColor::Cyan.on_default())
528+
.error(AnsiColor::Red.on_default() | Effects::BOLD)
529+
.valid(AnsiColor::Cyan.on_default() | Effects::BOLD)
530+
.invalid(AnsiColor::Yellow.on_default() | Effects::BOLD)
531+
};
532+
520533
Command::new("cargo")
521534
// Subcommands all count their args' display order independently (from 0),
522535
// which makes their args interspersed with global args. This puts global args last.
523536
//
524537
// We also want these to come before auto-generated `--help`
525538
.next_display_order(800)
526539
.allow_external_subcommands(true)
527-
// Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
528-
// opening clap up to allow us to style our help template
529-
.disable_colored_help(true)
540+
.styles(styles)
530541
// Provide a custom help subcommand for calling into man pages
531542
.disable_help_subcommand(true)
532543
.override_usage(usage)
533-
.help_template(
544+
.help_template(color_print::cstr!(
534545
"\
535546
Rust's package manager
536547
537-
Usage: {usage}
548+
<green,bold>Usage:</> {usage}
538549
539-
Options:
550+
<green,bold>Options:</>
540551
{options}
541552
542-
Commands:
543-
build, b Compile the current package
544-
check, c Analyze the current package and report errors, but don't build object files
545-
clean Remove the target directory
546-
doc, d Build this package's and its dependencies' documentation
547-
new Create a new cargo package
548-
init Create a new cargo package in an existing directory
549-
add Add dependencies to a manifest file
550-
remove Remove dependencies from a manifest file
551-
run, r Run a binary or example of the local package
552-
test, t Run the tests
553-
bench Run the benchmarks
554-
update Update dependencies listed in Cargo.lock
555-
search Search registry for crates
556-
publish Package and upload this package to the registry
557-
install Install a Rust binary. Default location is $HOME/.cargo/bin
558-
uninstall Uninstall a Rust binary
559-
... See all commands with --list
560-
561-
See 'cargo help <command>' for more information on a specific command.\n",
562-
)
553+
<green,bold>Commands:</>
554+
<cyan,bold>build</>, <cyan,bold>b</> Compile the current package
555+
<cyan,bold>check</>, <cyan,bold>c</> Analyze the current package and report errors, but don't build object files
556+
<cyan,bold>clean</> Remove the target directory
557+
<cyan,bold>doc</>, <cyan,bold>d</> Build this package's and its dependencies' documentation
558+
<cyan,bold>new</> Create a new cargo package
559+
<cyan,bold>init</> Create a new cargo package in an existing directory
560+
<cyan,bold>add</> Add dependencies to a manifest file
561+
<cyan,bold>remove</> Remove dependencies from a manifest file
562+
<cyan,bold>run</>, <cyan,bold>r</> Run a binary or example of the local package
563+
<cyan,bold>test</>, <cyan,bold>t</> Run the tests
564+
<cyan,bold>bench</> Run the benchmarks
565+
<cyan,bold>update</> Update dependencies listed in Cargo.lock
566+
<cyan,bold>search</> Search registry for crates
567+
<cyan,bold>publish</> Package and upload this package to the registry
568+
<cyan,bold>install</> Install a Rust binary. Default location is $HOME/.cargo/bin
569+
<cyan,bold>uninstall</> Uninstall a Rust binary
570+
<cyan>...</> See all commands with <cyan,bold>--list</>
571+
572+
See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a specific command.\n",
573+
))
563574
.arg(flag("version", "Print version info and exit").short('V'))
564575
.arg(flag("list", "List installed commands"))
565576
.arg(

src/bin/cargo/commands/add.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ pub fn cli() -> Command {
1818
clap::Command::new("add")
1919
.about("Add dependencies to a Cargo.toml manifest file")
2020
.override_usage(
21-
"\
22-
cargo add [OPTIONS] <DEP>[@<VERSION>] ...
23-
cargo add [OPTIONS] --path <PATH> ...
24-
cargo add [OPTIONS] --git <URL> ..."
25-
)
26-
.after_help("Run `cargo help add` for more detailed information.\n")
21+
color_print::cstr!("\
22+
<cyan,bold>cargo add</> <cyan>[OPTIONS] <<DEP>>[@<<VERSION>>] ...</>
23+
<cyan,bold>cargo add</> <cyan>[OPTIONS]</> <cyan,bold>--path</> <cyan><<PATH>> ...</>
24+
<cyan,bold>cargo add</> <cyan>[OPTIONS]</> <cyan,bold>--git</> <cyan><<URL>> ...</>"
25+
))
26+
.after_help(color_print::cstr!("Run `<cyan,bold>cargo help add</>` for more detailed information.\n"))
2727
.group(clap::ArgGroup::new("selected").multiple(true).required(true))
2828
.args([
2929
clap::Arg::new("crates")

src/bin/cargo/commands/bench.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ pub fn cli() -> Command {
5050
.arg_unit_graph()
5151
.arg_timings()
5252
.arg_manifest_path()
53-
.after_help("Run `cargo help bench` for more detailed information.\n")
53+
.after_help(color_print::cstr!(
54+
"Run `<cyan,bold>cargo help bench</>` for more detailed information.\n"
55+
))
5456
}
5557

5658
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/build.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ pub fn cli() -> Command {
4646
.arg_unit_graph()
4747
.arg_timings()
4848
.arg_manifest_path()
49-
.after_help("Run `cargo help build` for more detailed information.\n")
49+
.after_help(color_print::cstr!(
50+
"Run `<cyan,bold>cargo help build</>` for more detailed information.\n"
51+
))
5052
}
5153

5254
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/check.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ pub fn cli() -> Command {
3737
.arg_unit_graph()
3838
.arg_timings()
3939
.arg_manifest_path()
40-
.after_help("Run `cargo help check` for more detailed information.\n")
40+
.after_help(color_print::cstr!(
41+
"Run `<cyan,bold>cargo help check</>` for more detailed information.\n"
42+
))
4143
}
4244

4345
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/clean.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ pub fn cli() -> Command {
1414
.arg_target_triple("Target triple to clean output for")
1515
.arg_target_dir()
1616
.arg_manifest_path()
17-
.after_help("Run `cargo help clean` for more detailed information.\n")
17+
.after_help(color_print::cstr!(
18+
"Run `<cyan,bold>cargo help clean</>` for more detailed information.\n"
19+
))
1820
}
1921

2022
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/doc.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ pub fn cli() -> Command {
4040
.arg_unit_graph()
4141
.arg_timings()
4242
.arg_manifest_path()
43-
.after_help("Run `cargo help doc` for more detailed information.\n")
43+
.after_help(color_print::cstr!(
44+
"Run `<cyan,bold>cargo help doc</>` for more detailed information.\n"
45+
))
4446
}
4547

4648
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/fetch.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub fn cli() -> Command {
99
.arg_quiet()
1010
.arg_target_triple("Fetch dependencies for the target triple")
1111
.arg_manifest_path()
12-
.after_help("Run `cargo help fetch` for more detailed information.\n")
12+
.after_help(color_print::cstr!(
13+
"Run `<cyan,bold>cargo help fetch</>` for more detailed information.\n"
14+
))
1315
}
1416

1517
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/fix.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ pub fn cli() -> Command {
5454
.arg_target_dir()
5555
.arg_timings()
5656
.arg_manifest_path()
57-
.after_help("Run `cargo help fix` for more detailed information.\n")
57+
.after_help(color_print::cstr!(
58+
"Run `<cyan,bold>cargo help fix</>` for more detailed information.\n"
59+
))
5860
}
5961

6062
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/generate_lockfile.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ pub fn cli() -> Command {
77
.about("Generate the lockfile for a package")
88
.arg_quiet()
99
.arg_manifest_path()
10-
.after_help("Run `cargo help generate-lockfile` for more detailed information.\n")
10+
.after_help(color_print::cstr!(
11+
"Run `<cyan,bold>cargo help generate-lockfile</>` for more detailed information.\n"
12+
))
1113
}
1214

1315
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/init.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub fn cli() -> Command {
99
.arg_new_opts()
1010
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
1111
.arg_quiet()
12-
.after_help("Run `cargo help init` for more detailed information.\n")
12+
.after_help(color_print::cstr!(
13+
"Run `<cyan,bold>cargo help init</>` for more detailed information.\n"
14+
))
1315
}
1416

1517
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/install.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ pub fn cli() -> Command {
8787
.arg_target_triple("Build for the target triple")
8888
.arg_target_dir()
8989
.arg_timings()
90-
.after_help("Run `cargo help install` for more detailed information.\n")
90+
.after_help(color_print::cstr!(
91+
"Run `<cyan,bold>cargo help install</>` for more detailed information.\n"
92+
))
9193
}
9294

9395
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/locate_project.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ pub fn cli() -> Command {
1616
)
1717
.arg_quiet()
1818
.arg_manifest_path()
19-
.after_help("Run `cargo help locate-project` for more detailed information.\n")
19+
.after_help(color_print::cstr!(
20+
"Run `<cyan,bold>cargo help locate-project</>` for more detailed information.\n"
21+
))
2022
}
2123

2224
#[derive(Serialize)]

src/bin/cargo/commands/login.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ pub fn cli() -> Command {
1414
.last(true),
1515
)
1616
.arg_quiet()
17-
.after_help("Run `cargo help login` for more detailed information.\n")
17+
.after_help(color_print::cstr!(
18+
"Run `<cyan,bold>cargo help login</>` for more detailed information.\n"
19+
))
1820
}
1921

2022
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/logout.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ pub fn cli() -> Command {
66
.about("Remove an API token from the registry locally")
77
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
88
.arg_quiet()
9-
.after_help("Run `cargo help logout` for more detailed information.\n")
9+
.after_help(color_print::cstr!(
10+
"Run `<cyan,bold>cargo help logout</>` for more detailed information.\n"
11+
))
1012
}
1113

1214
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/metadata.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ pub fn cli() -> Command {
2626
.arg_quiet()
2727
.arg_features()
2828
.arg_manifest_path()
29-
.after_help("Run `cargo help metadata` for more detailed information.\n")
29+
.after_help(color_print::cstr!(
30+
"Run `<cyan,bold>cargo help metadata</>` for more detailed information.\n"
31+
))
3032
}
3133

3234
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/new.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ pub fn cli() -> Command {
99
.arg_new_opts()
1010
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
1111
.arg_quiet()
12-
.after_help("Run `cargo help new` for more detailed information.\n")
12+
.after_help(color_print::cstr!(
13+
"Run `<cyan,bold>cargo help new</>` for more detailed information.\n"
14+
))
1315
}
1416

1517
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/owner.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ pub fn cli() -> Command {
2828
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
2929
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
3030
.arg_quiet()
31-
.after_help("Run `cargo help owner` for more detailed information.\n")
31+
.after_help(color_print::cstr!(
32+
"Run `<cyan,bold>cargo help owner</>` for more detailed information.\n"
33+
))
3234
}
3335

3436
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/package.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ pub fn cli() -> Command {
3535
.arg_target_dir()
3636
.arg_parallel()
3737
.arg_manifest_path()
38-
.after_help("Run `cargo help package` for more detailed information.\n")
38+
.after_help(color_print::cstr!(
39+
"Run `<cyan,bold>cargo help package</>` for more detailed information.\n"
40+
))
3941
}
4042

4143
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/pkgid.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ pub fn cli() -> Command {
1010
.arg_quiet()
1111
.arg_package("Argument to get the package ID specifier for")
1212
.arg_manifest_path()
13-
.after_help("Run `cargo help pkgid` for more detailed information.\n")
13+
.after_help(color_print::cstr!(
14+
"Run `<cyan,bold>cargo help pkgid</>` for more detailed information.\n"
15+
))
1416
}
1517

1618
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/publish.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ pub fn cli() -> Command {
2424
.arg_target_triple("Build for the target triple")
2525
.arg_target_dir()
2626
.arg_manifest_path()
27-
.after_help("Run `cargo help publish` for more detailed information.\n")
27+
.after_help(color_print::cstr!(
28+
"Run `<cyan,bold>cargo help publish</>` for more detailed information.\n"
29+
))
2830
}
2931

3032
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {

src/bin/cargo/commands/read_manifest.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use crate::command_prelude::*;
22

33
pub fn cli() -> Command {
44
subcommand("read-manifest")
5-
.about(
5+
.about(color_print::cstr!(
66
"\
77
Print a JSON representation of a Cargo.toml manifest.
88
9-
Deprecated, use `cargo metadata --no-deps` instead.\
10-
",
11-
)
9+
Deprecated, use `<cyan,bold>cargo metadata --no-deps</>` instead.\
10+
"
11+
))
1212
.arg_quiet()
1313
.arg_manifest_path()
1414
}

0 commit comments

Comments
 (0)