Skip to content

Commit

Permalink
feat: Add App::next_help_heading
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
epage committed Feb 8, 2022
1 parent 4f50e97 commit c00f71e
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 39 deletions.
13 changes: 10 additions & 3 deletions clap_derive/src/attrs.rs
Expand Up @@ -46,6 +46,7 @@ pub struct Attrs {
author: Option<Method>,
version: Option<Method>,
verbatim_doc_comment: Option<Ident>,
next_help_heading: Option<Method>,
help_heading: Option<Method>,
is_enum: bool,
has_custom_parser: bool,
Expand Down Expand Up @@ -379,6 +380,7 @@ impl Attrs {
author: None,
version: None,
verbatim_doc_comment: None,
next_help_heading: None,
help_heading: None,
is_enum: false,
has_custom_parser: false,
Expand Down Expand Up @@ -535,6 +537,9 @@ impl Attrs {
HelpHeading(ident, expr) => {
self.help_heading = Some(Method::new(ident, quote!(#expr)));
}
NextHelpHeading(ident, expr) => {
self.next_help_heading = Some(Method::new(ident, quote!(#expr)));
}

About(ident, about) => {
if let Some(method) =
Expand Down Expand Up @@ -623,8 +628,9 @@ impl Attrs {

/// generate methods from attributes on top of struct or enum
pub fn initial_top_level_methods(&self) -> TokenStream {
let next_help_heading = self.next_help_heading.as_ref().into_iter();
let help_heading = self.help_heading.as_ref().into_iter();
quote!( #(#help_heading)* )
quote!( #(#next_help_heading)* #(#help_heading)* )
}

pub fn final_top_level_methods(&self) -> TokenStream {
Expand Down Expand Up @@ -655,9 +661,10 @@ impl Attrs {
}
}

pub fn help_heading(&self) -> TokenStream {
pub fn next_help_heading(&self) -> TokenStream {
let next_help_heading = self.next_help_heading.as_ref().into_iter();
let help_heading = self.help_heading.as_ref().into_iter();
quote!( #(#help_heading)* )
quote!( #(#next_help_heading)* #(#help_heading)* )
}

pub fn cased_name(&self) -> TokenStream {
Expand Down
14 changes: 7 additions & 7 deletions clap_derive/src/derives/args.rs
Expand Up @@ -213,20 +213,20 @@ pub fn gen_augment(
Kind::Flatten => {
let ty = &field.ty;
let old_heading_var = format_ident!("__clap_old_heading");
let help_heading = attrs.help_heading();
let next_help_heading = attrs.next_help_heading();
if override_required {
Some(quote_spanned! { kind.span()=>
let #old_heading_var = #app_var.get_help_heading();
let #app_var = #app_var #help_heading;
let #old_heading_var = #app_var.get_next_help_heading();
let #app_var = #app_var #next_help_heading;
let #app_var = <#ty as clap::Args>::augment_args_for_update(#app_var);
let #app_var = #app_var.help_heading(#old_heading_var);
let #app_var = #app_var.next_help_heading(#old_heading_var);
})
} else {
Some(quote_spanned! { kind.span()=>
let #old_heading_var = #app_var.get_help_heading();
let #app_var = #app_var #help_heading;
let #old_heading_var = #app_var.get_next_help_heading();
let #app_var = #app_var #next_help_heading;
let #app_var = <#ty as clap::Args>::augment_args(#app_var);
let #app_var = #app_var.help_heading(#old_heading_var);
let #app_var = #app_var.next_help_heading(#old_heading_var);
})
}
}
Expand Down
14 changes: 7 additions & 7 deletions clap_derive/src/derives/subcommand.rs
Expand Up @@ -187,20 +187,20 @@ fn gen_augment(
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
let old_heading_var = format_ident!("__clap_old_heading");
let help_heading = attrs.help_heading();
let next_help_heading = attrs.next_help_heading();
let subcommand = if override_required {
quote! {
let #old_heading_var = #app_var.get_help_heading();
let #app_var = #app_var #help_heading;
let #old_heading_var = #app_var.get_next_help_heading();
let #app_var = #app_var #next_help_heading;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
let #app_var = #app_var.help_heading(#old_heading_var);
let #app_var = #app_var.next_help_heading(#old_heading_var);
}
} else {
quote! {
let #old_heading_var = #app_var.get_help_heading();
let #app_var = #app_var #help_heading;
let #old_heading_var = #app_var.get_next_help_heading();
let #app_var = #app_var #next_help_heading;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
let #app_var = #app_var.help_heading(#old_heading_var);
let #app_var = #app_var.next_help_heading(#old_heading_var);
}
};
Some(subcommand)
Expand Down
10 changes: 10 additions & 0 deletions clap_derive/src/parse.rs
Expand Up @@ -54,6 +54,7 @@ pub enum ClapAttr {
NameExpr(Ident, Expr),
DefaultValueT(Ident, Option<Expr>),
DefaultValueOsT(Ident, Option<Expr>),
NextHelpHeading(Ident, Expr),
HelpHeading(Ident, Expr),

// ident(arbitrary_expr,*)
Expand Down Expand Up @@ -114,6 +115,14 @@ impl Parse for ClapAttr {
Ok(Skip(name, Some(expr)))
}

"next_help_heading" => {
let expr = ExprLit {
attrs: vec![],
lit: Lit::Str(lit),
};
let expr = Expr::Lit(expr);
Ok(NextHelpHeading(name, expr))
}
"help_heading" => {
let expr = ExprLit {
attrs: vec![],
Expand All @@ -131,6 +140,7 @@ impl Parse for ClapAttr {
"skip" => Ok(Skip(name, Some(expr))),
"default_value_t" => Ok(DefaultValueT(name, Some(expr))),
"default_value_os_t" => Ok(DefaultValueOsT(name, Some(expr))),
"next_help_heading" => Ok(NextHelpHeading(name, expr)),
"help_heading" => Ok(HelpHeading(name, expr)),
_ => Ok(NameExpr(name, expr)),
},
Expand Down
2 changes: 1 addition & 1 deletion examples/derive_ref/README.md
Expand Up @@ -130,7 +130,7 @@ In addition to the raw attributes, the following magic attributes are supported:
- `long_about = <expr>`: `clap::App::long_about`
- When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
- `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to `about` / `long_about`
- `help_heading`: `clap::App::help_heading`
- `next_help_heading`: `clap::App::next_help_heading`
- When `flatten`ing `Args`, this is scoped to just the args in this struct and any struct `flatten`ed into it
- `rename_all = <expr>`: Override default field / variant name case conversion for `App::name` / `Arg::name`
- When not present: `kebab-case`
Expand Down
32 changes: 31 additions & 1 deletion src/build/app/mod.rs
Expand Up @@ -1333,7 +1333,28 @@ impl<'help> App<'help> {
/// [`Arg::help_heading`]: crate::Arg::help_heading()
#[inline]
#[must_use]
pub fn help_heading<O>(mut self, heading: O) -> Self
// TODO: Deprecate
pub fn help_heading<O>(self, heading: O) -> Self
where
O: Into<Option<&'help str>>,
{
self.next_help_heading(heading)
}

/// Set the default section heading for future args.
///
/// This will be used for any arg that hasn't had [`Arg::help_heading`] called.
///
/// This is useful if the default `OPTIONS` or `ARGS` headings are
/// not specific enough for one's use case.
///
/// For subcommands, see [`App::subcommand_help_heading`]
///
/// [`App::arg`]: App::arg()
/// [`Arg::help_heading`]: crate::Arg::help_heading()
#[inline]
#[must_use]
pub fn next_help_heading<O>(mut self, heading: O) -> Self
where
O: Into<Option<&'help str>>,
{
Expand Down Expand Up @@ -2165,7 +2186,16 @@ impl<'help> App<'help> {
///
/// [`App::help_heading`]: App::help_heading()
#[inline]
// TODO: Deprecate
pub fn get_help_heading(&self) -> Option<&'help str> {
self.get_next_help_heading()
}

/// Get the custom section heading specified via [`App::help_heading`].
///
/// [`App::help_heading`]: App::help_heading()
#[inline]
pub fn get_next_help_heading(&self) -> Option<&'help str> {
self.current_help_heading
}

Expand Down
20 changes: 10 additions & 10 deletions tests/builder/help.rs
Expand Up @@ -1785,7 +1785,7 @@ fn custom_headers_headers() {
.require_delimiter(true)
.value_delimiter(':'),
)
.help_heading(Some("NETWORKING"))
.next_help_heading(Some("NETWORKING"))
.arg(
Arg::new("no-proxy")
.short('n')
Expand Down Expand Up @@ -1842,14 +1842,14 @@ fn multiple_custom_help_headers() {
.require_delimiter(true)
.value_delimiter(':'),
)
.help_heading(Some("NETWORKING"))
.next_help_heading(Some("NETWORKING"))
.arg(
Arg::new("no-proxy")
.short('n')
.long("no-proxy")
.help("Do not use system proxy settings"),
)
.help_heading(Some("SPECIAL"))
.next_help_heading(Some("SPECIAL"))
.arg(
arg!(-b --"birthday-song" <song> "Change which song is played for birthdays")
.help_heading(Some("OVERRIDE SPECIAL")),
Expand All @@ -1862,7 +1862,7 @@ fn multiple_custom_help_headers() {
.arg(arg!(
-v --"birthday-song-volume" <volume> "Change the volume of the birthday song"
))
.help_heading(None)
.next_help_heading(None)
.arg(
Arg::new("server-addr")
.short('a')
Expand Down Expand Up @@ -1912,23 +1912,23 @@ fn custom_help_headers_hide_args() {
.author("Will M.")
.about("does stuff")
.version("1.4")
.help_heading(Some("NETWORKING"))
.next_help_heading(Some("NETWORKING"))
.arg(
Arg::new("no-proxy")
.short('n')
.long("no-proxy")
.help("Do not use system proxy settings")
.hide_short_help(true),
)
.help_heading(Some("SPECIAL"))
.next_help_heading(Some("SPECIAL"))
.arg(
arg!(-b --song <song> "Change which song is played for birthdays")
.help_heading(Some("OVERRIDE SPECIAL")),
)
.arg(arg!(
-v --"song-volume" <volume> "Change the volume of the birthday song"
))
.help_heading(None)
.next_help_heading(None)
.arg(
Arg::new("server-addr")
.short('a')
Expand Down Expand Up @@ -2392,7 +2392,7 @@ fn custom_heading_pos() {
let app = App::new("test")
.version("1.4")
.arg(Arg::new("gear").help("Which gear"))
.help_heading(Some("NETWORKING"))
.next_help_heading(Some("NETWORKING"))
.arg(Arg::new("speed").help("How fast"));

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

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

assert!(utils::compare_output(
Expand Down
6 changes: 3 additions & 3 deletions tests/builder/opts.rs
Expand Up @@ -561,7 +561,7 @@ fn long_eq_val_starts_with_eq() {
#[test]
fn issue_2022_get_flags_misuse() {
let app = App::new("test")
.help_heading(Some("test"))
.next_help_heading(Some("test"))
.arg(Arg::new("a").long("a").default_value("32"));
let matches = app.try_get_matches_from(&[""]).unwrap();
assert!(matches.value_of("a").is_some())
Expand All @@ -571,14 +571,14 @@ fn issue_2022_get_flags_misuse() {
fn issue_2279() {
let before_help_heading = App::new("app")
.arg(Arg::new("foo").short('f').default_value("bar"))
.help_heading(Some("This causes default_value to be ignored"))
.next_help_heading(Some("This causes default_value to be ignored"))
.try_get_matches_from(&[""])
.unwrap();

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

let after_help_heading = App::new("app")
.help_heading(Some("This causes default_value to be ignored"))
.next_help_heading(Some("This causes default_value to be ignored"))
.arg(Arg::new("foo").short('f').default_value("bar"))
.try_get_matches_from(&[""])
.unwrap();
Expand Down
14 changes: 7 additions & 7 deletions tests/derive/help.rs
Expand Up @@ -30,7 +30,7 @@ fn arg_help_heading_applied() {
#[test]
fn app_help_heading_applied() {
#[derive(Debug, Clone, Parser)]
#[clap(help_heading = "DEFAULT")]
#[clap(next_help_heading = "DEFAULT")]
struct CliOptions {
#[clap(long)]
#[clap(help_heading = Some("HEADING A"))]
Expand Down Expand Up @@ -79,14 +79,14 @@ fn app_help_heading_flattened() {
}

#[derive(Debug, Clone, Args)]
#[clap(help_heading = "HEADING A")]
#[clap(next_help_heading = "HEADING A")]
struct OptionsA {
#[clap(long)]
should_be_in_section_a: u32,
}

#[derive(Debug, Clone, Args)]
#[clap(help_heading = "HEADING B")]
#[clap(next_help_heading = "HEADING B")]
struct OptionsB {
#[clap(long)]
should_be_in_section_b: u32,
Expand All @@ -99,21 +99,21 @@ fn app_help_heading_flattened() {
#[clap(subcommand)]
SubC(SubC),
SubAOne,
#[clap(help_heading = "SUB A")]
#[clap(next_help_heading = "SUB A")]
SubATwo {
should_be_in_sub_a: u32,
},
}

#[derive(Debug, Clone, Subcommand)]
enum SubB {
#[clap(help_heading = "SUB B")]
#[clap(next_help_heading = "SUB B")]
SubBOne { should_be_in_sub_b: u32 },
}

#[derive(Debug, Clone, Subcommand)]
enum SubC {
#[clap(help_heading = "SUB C")]
#[clap(next_help_heading = "SUB C")]
SubCOne { should_be_in_sub_c: u32 },
}

Expand Down Expand Up @@ -168,7 +168,7 @@ fn flatten_field_with_help_heading() {
#[derive(Debug, Clone, Parser)]
struct CliOptions {
#[clap(flatten)]
#[clap(help_heading = "HEADING A")]
#[clap(next_help_heading = "HEADING A")]
options_a: OptionsA,
}

Expand Down

0 comments on commit c00f71e

Please sign in to comment.