Skip to content

Commit c00f71e

Browse files
committed
feat: Add App::next_help_heading
This clarifies the intent and prepares for other functions doing the same, like `next_display_order`. This will then open us to name `subcommand_help_heading` and `display_order` similar. The deprecation is waiting on 3.1. This is part of clap-rs#1807 and clap-rs#1553.
1 parent 4f50e97 commit c00f71e

File tree

9 files changed

+86
-39
lines changed

9 files changed

+86
-39
lines changed

clap_derive/src/attrs.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct Attrs {
4646
author: Option<Method>,
4747
version: Option<Method>,
4848
verbatim_doc_comment: Option<Ident>,
49+
next_help_heading: Option<Method>,
4950
help_heading: Option<Method>,
5051
is_enum: bool,
5152
has_custom_parser: bool,
@@ -379,6 +380,7 @@ impl Attrs {
379380
author: None,
380381
version: None,
381382
verbatim_doc_comment: None,
383+
next_help_heading: None,
382384
help_heading: None,
383385
is_enum: false,
384386
has_custom_parser: false,
@@ -535,6 +537,9 @@ impl Attrs {
535537
HelpHeading(ident, expr) => {
536538
self.help_heading = Some(Method::new(ident, quote!(#expr)));
537539
}
540+
NextHelpHeading(ident, expr) => {
541+
self.next_help_heading = Some(Method::new(ident, quote!(#expr)));
542+
}
538543

539544
About(ident, about) => {
540545
if let Some(method) =
@@ -623,8 +628,9 @@ impl Attrs {
623628

624629
/// generate methods from attributes on top of struct or enum
625630
pub fn initial_top_level_methods(&self) -> TokenStream {
631+
let next_help_heading = self.next_help_heading.as_ref().into_iter();
626632
let help_heading = self.help_heading.as_ref().into_iter();
627-
quote!( #(#help_heading)* )
633+
quote!( #(#next_help_heading)* #(#help_heading)* )
628634
}
629635

630636
pub fn final_top_level_methods(&self) -> TokenStream {
@@ -655,9 +661,10 @@ impl Attrs {
655661
}
656662
}
657663

658-
pub fn help_heading(&self) -> TokenStream {
664+
pub fn next_help_heading(&self) -> TokenStream {
665+
let next_help_heading = self.next_help_heading.as_ref().into_iter();
659666
let help_heading = self.help_heading.as_ref().into_iter();
660-
quote!( #(#help_heading)* )
667+
quote!( #(#next_help_heading)* #(#help_heading)* )
661668
}
662669

663670
pub fn cased_name(&self) -> TokenStream {

clap_derive/src/derives/args.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,20 @@ pub fn gen_augment(
213213
Kind::Flatten => {
214214
let ty = &field.ty;
215215
let old_heading_var = format_ident!("__clap_old_heading");
216-
let help_heading = attrs.help_heading();
216+
let next_help_heading = attrs.next_help_heading();
217217
if override_required {
218218
Some(quote_spanned! { kind.span()=>
219-
let #old_heading_var = #app_var.get_help_heading();
220-
let #app_var = #app_var #help_heading;
219+
let #old_heading_var = #app_var.get_next_help_heading();
220+
let #app_var = #app_var #next_help_heading;
221221
let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var);
222-
let #app_var = #app_var.help_heading(#old_heading_var);
222+
let #app_var = #app_var.next_help_heading(#old_heading_var);
223223
})
224224
} else {
225225
Some(quote_spanned! { kind.span()=>
226-
let #old_heading_var = #app_var.get_help_heading();
227-
let #app_var = #app_var #help_heading;
226+
let #old_heading_var = #app_var.get_next_help_heading();
227+
let #app_var = #app_var #next_help_heading;
228228
let #app_var = <#ty as clap::Args>::augment_args(#app_var);
229-
let #app_var = #app_var.help_heading(#old_heading_var);
229+
let #app_var = #app_var.next_help_heading(#old_heading_var);
230230
})
231231
}
232232
}

clap_derive/src/derives/subcommand.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,20 @@ fn gen_augment(
187187
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
188188
let ty = &unnamed[0];
189189
let old_heading_var = format_ident!("__clap_old_heading");
190-
let help_heading = attrs.help_heading();
190+
let next_help_heading = attrs.next_help_heading();
191191
let subcommand = if override_required {
192192
quote! {
193-
let #old_heading_var = #app_var.get_help_heading();
194-
let #app_var = #app_var #help_heading;
193+
let #old_heading_var = #app_var.get_next_help_heading();
194+
let #app_var = #app_var #next_help_heading;
195195
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
196-
let #app_var = #app_var.help_heading(#old_heading_var);
196+
let #app_var = #app_var.next_help_heading(#old_heading_var);
197197
}
198198
} else {
199199
quote! {
200-
let #old_heading_var = #app_var.get_help_heading();
201-
let #app_var = #app_var #help_heading;
200+
let #old_heading_var = #app_var.get_next_help_heading();
201+
let #app_var = #app_var #next_help_heading;
202202
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
203-
let #app_var = #app_var.help_heading(#old_heading_var);
203+
let #app_var = #app_var.next_help_heading(#old_heading_var);
204204
}
205205
};
206206
Some(subcommand)

clap_derive/src/parse.rs

+10
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ pub enum ClapAttr {
5454
NameExpr(Ident, Expr),
5555
DefaultValueT(Ident, Option<Expr>),
5656
DefaultValueOsT(Ident, Option<Expr>),
57+
NextHelpHeading(Ident, Expr),
5758
HelpHeading(Ident, Expr),
5859

5960
// ident(arbitrary_expr,*)
@@ -114,6 +115,14 @@ impl Parse for ClapAttr {
114115
Ok(Skip(name, Some(expr)))
115116
}
116117

118+
"next_help_heading" => {
119+
let expr = ExprLit {
120+
attrs: vec![],
121+
lit: Lit::Str(lit),
122+
};
123+
let expr = Expr::Lit(expr);
124+
Ok(NextHelpHeading(name, expr))
125+
}
117126
"help_heading" => {
118127
let expr = ExprLit {
119128
attrs: vec![],
@@ -131,6 +140,7 @@ impl Parse for ClapAttr {
131140
"skip" => Ok(Skip(name, Some(expr))),
132141
"default_value_t" => Ok(DefaultValueT(name, Some(expr))),
133142
"default_value_os_t" => Ok(DefaultValueOsT(name, Some(expr))),
143+
"next_help_heading" => Ok(NextHelpHeading(name, expr)),
134144
"help_heading" => Ok(HelpHeading(name, expr)),
135145
_ => Ok(NameExpr(name, expr)),
136146
},

examples/derive_ref/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ In addition to the raw attributes, the following magic attributes are supported:
130130
- `long_about = <expr>`: `clap::App::long_about`
131131
- When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
132132
- `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to `about` / `long_about`
133-
- `help_heading`: `clap::App::help_heading`
133+
- `next_help_heading`: `clap::App::next_help_heading`
134134
- When `flatten`ing `Args`, this is scoped to just the args in this struct and any struct `flatten`ed into it
135135
- `rename_all = <expr>`: Override default field / variant name case conversion for `App::name` / `Arg::name`
136136
- When not present: `kebab-case`

src/build/app/mod.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1333,7 +1333,28 @@ impl<'help> App<'help> {
13331333
/// [`Arg::help_heading`]: crate::Arg::help_heading()
13341334
#[inline]
13351335
#[must_use]
1336-
pub fn help_heading<O>(mut self, heading: O) -> Self
1336+
// TODO: Deprecate
1337+
pub fn help_heading<O>(self, heading: O) -> Self
1338+
where
1339+
O: Into<Option<&'help str>>,
1340+
{
1341+
self.next_help_heading(heading)
1342+
}
1343+
1344+
/// Set the default section heading for future args.
1345+
///
1346+
/// This will be used for any arg that hasn't had [`Arg::help_heading`] called.
1347+
///
1348+
/// This is useful if the default `OPTIONS` or `ARGS` headings are
1349+
/// not specific enough for one's use case.
1350+
///
1351+
/// For subcommands, see [`App::subcommand_help_heading`]
1352+
///
1353+
/// [`App::arg`]: App::arg()
1354+
/// [`Arg::help_heading`]: crate::Arg::help_heading()
1355+
#[inline]
1356+
#[must_use]
1357+
pub fn next_help_heading<O>(mut self, heading: O) -> Self
13371358
where
13381359
O: Into<Option<&'help str>>,
13391360
{
@@ -2165,7 +2186,16 @@ impl<'help> App<'help> {
21652186
///
21662187
/// [`App::help_heading`]: App::help_heading()
21672188
#[inline]
2189+
// TODO: Deprecate
21682190
pub fn get_help_heading(&self) -> Option<&'help str> {
2191+
self.get_next_help_heading()
2192+
}
2193+
2194+
/// Get the custom section heading specified via [`App::help_heading`].
2195+
///
2196+
/// [`App::help_heading`]: App::help_heading()
2197+
#[inline]
2198+
pub fn get_next_help_heading(&self) -> Option<&'help str> {
21692199
self.current_help_heading
21702200
}
21712201

tests/builder/help.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,7 @@ fn custom_headers_headers() {
17851785
.require_delimiter(true)
17861786
.value_delimiter(':'),
17871787
)
1788-
.help_heading(Some("NETWORKING"))
1788+
.next_help_heading(Some("NETWORKING"))
17891789
.arg(
17901790
Arg::new("no-proxy")
17911791
.short('n')
@@ -1842,14 +1842,14 @@ fn multiple_custom_help_headers() {
18421842
.require_delimiter(true)
18431843
.value_delimiter(':'),
18441844
)
1845-
.help_heading(Some("NETWORKING"))
1845+
.next_help_heading(Some("NETWORKING"))
18461846
.arg(
18471847
Arg::new("no-proxy")
18481848
.short('n')
18491849
.long("no-proxy")
18501850
.help("Do not use system proxy settings"),
18511851
)
1852-
.help_heading(Some("SPECIAL"))
1852+
.next_help_heading(Some("SPECIAL"))
18531853
.arg(
18541854
arg!(-b --"birthday-song" <song> "Change which song is played for birthdays")
18551855
.help_heading(Some("OVERRIDE SPECIAL")),
@@ -1862,7 +1862,7 @@ fn multiple_custom_help_headers() {
18621862
.arg(arg!(
18631863
-v --"birthday-song-volume" <volume> "Change the volume of the birthday song"
18641864
))
1865-
.help_heading(None)
1865+
.next_help_heading(None)
18661866
.arg(
18671867
Arg::new("server-addr")
18681868
.short('a')
@@ -1912,23 +1912,23 @@ fn custom_help_headers_hide_args() {
19121912
.author("Will M.")
19131913
.about("does stuff")
19141914
.version("1.4")
1915-
.help_heading(Some("NETWORKING"))
1915+
.next_help_heading(Some("NETWORKING"))
19161916
.arg(
19171917
Arg::new("no-proxy")
19181918
.short('n')
19191919
.long("no-proxy")
19201920
.help("Do not use system proxy settings")
19211921
.hide_short_help(true),
19221922
)
1923-
.help_heading(Some("SPECIAL"))
1923+
.next_help_heading(Some("SPECIAL"))
19241924
.arg(
19251925
arg!(-b --song <song> "Change which song is played for birthdays")
19261926
.help_heading(Some("OVERRIDE SPECIAL")),
19271927
)
19281928
.arg(arg!(
19291929
-v --"song-volume" <volume> "Change the volume of the birthday song"
19301930
))
1931-
.help_heading(None)
1931+
.next_help_heading(None)
19321932
.arg(
19331933
Arg::new("server-addr")
19341934
.short('a')
@@ -2392,7 +2392,7 @@ fn custom_heading_pos() {
23922392
let app = App::new("test")
23932393
.version("1.4")
23942394
.arg(Arg::new("gear").help("Which gear"))
2395-
.help_heading(Some("NETWORKING"))
2395+
.next_help_heading(Some("NETWORKING"))
23962396
.arg(Arg::new("speed").help("How fast"));
23972397

23982398
assert!(utils::compare_output(
@@ -2418,7 +2418,7 @@ fn only_custom_heading_opts_no_args() {
24182418
.version("1.4")
24192419
.setting(AppSettings::DisableVersionFlag)
24202420
.mut_arg("help", |a| a.hide(true))
2421-
.help_heading(Some("NETWORKING"))
2421+
.next_help_heading(Some("NETWORKING"))
24222422
.arg(arg!(-s --speed <SPEED> "How fast").required(false));
24232423

24242424
assert!(utils::compare_output(
@@ -2444,7 +2444,7 @@ fn only_custom_heading_pos_no_args() {
24442444
.version("1.4")
24452445
.setting(AppSettings::DisableVersionFlag)
24462446
.mut_arg("help", |a| a.hide(true))
2447-
.help_heading(Some("NETWORKING"))
2447+
.next_help_heading(Some("NETWORKING"))
24482448
.arg(Arg::new("speed").help("How fast"));
24492449

24502450
assert!(utils::compare_output(

tests/builder/opts.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ fn long_eq_val_starts_with_eq() {
561561
#[test]
562562
fn issue_2022_get_flags_misuse() {
563563
let app = App::new("test")
564-
.help_heading(Some("test"))
564+
.next_help_heading(Some("test"))
565565
.arg(Arg::new("a").long("a").default_value("32"));
566566
let matches = app.try_get_matches_from(&[""]).unwrap();
567567
assert!(matches.value_of("a").is_some())
@@ -571,14 +571,14 @@ fn issue_2022_get_flags_misuse() {
571571
fn issue_2279() {
572572
let before_help_heading = App::new("app")
573573
.arg(Arg::new("foo").short('f').default_value("bar"))
574-
.help_heading(Some("This causes default_value to be ignored"))
574+
.next_help_heading(Some("This causes default_value to be ignored"))
575575
.try_get_matches_from(&[""])
576576
.unwrap();
577577

578578
assert_eq!(before_help_heading.value_of("foo"), Some("bar"));
579579

580580
let after_help_heading = App::new("app")
581-
.help_heading(Some("This causes default_value to be ignored"))
581+
.next_help_heading(Some("This causes default_value to be ignored"))
582582
.arg(Arg::new("foo").short('f').default_value("bar"))
583583
.try_get_matches_from(&[""])
584584
.unwrap();

tests/derive/help.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn arg_help_heading_applied() {
3030
#[test]
3131
fn app_help_heading_applied() {
3232
#[derive(Debug, Clone, Parser)]
33-
#[clap(help_heading = "DEFAULT")]
33+
#[clap(next_help_heading = "DEFAULT")]
3434
struct CliOptions {
3535
#[clap(long)]
3636
#[clap(help_heading = Some("HEADING A"))]
@@ -79,14 +79,14 @@ fn app_help_heading_flattened() {
7979
}
8080

8181
#[derive(Debug, Clone, Args)]
82-
#[clap(help_heading = "HEADING A")]
82+
#[clap(next_help_heading = "HEADING A")]
8383
struct OptionsA {
8484
#[clap(long)]
8585
should_be_in_section_a: u32,
8686
}
8787

8888
#[derive(Debug, Clone, Args)]
89-
#[clap(help_heading = "HEADING B")]
89+
#[clap(next_help_heading = "HEADING B")]
9090
struct OptionsB {
9191
#[clap(long)]
9292
should_be_in_section_b: u32,
@@ -99,21 +99,21 @@ fn app_help_heading_flattened() {
9999
#[clap(subcommand)]
100100
SubC(SubC),
101101
SubAOne,
102-
#[clap(help_heading = "SUB A")]
102+
#[clap(next_help_heading = "SUB A")]
103103
SubATwo {
104104
should_be_in_sub_a: u32,
105105
},
106106
}
107107

108108
#[derive(Debug, Clone, Subcommand)]
109109
enum SubB {
110-
#[clap(help_heading = "SUB B")]
110+
#[clap(next_help_heading = "SUB B")]
111111
SubBOne { should_be_in_sub_b: u32 },
112112
}
113113

114114
#[derive(Debug, Clone, Subcommand)]
115115
enum SubC {
116-
#[clap(help_heading = "SUB C")]
116+
#[clap(next_help_heading = "SUB C")]
117117
SubCOne { should_be_in_sub_c: u32 },
118118
}
119119

@@ -168,7 +168,7 @@ fn flatten_field_with_help_heading() {
168168
#[derive(Debug, Clone, Parser)]
169169
struct CliOptions {
170170
#[clap(flatten)]
171-
#[clap(help_heading = "HEADING A")]
171+
#[clap(next_help_heading = "HEADING A")]
172172
options_a: OptionsA,
173173
}
174174

0 commit comments

Comments
 (0)