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

feat: Help Heading and Display Order work in prep for deprecations #3414

Merged
merged 5 commits into from
Feb 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions clap_derive/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct Attrs {
author: Option<Method>,
version: Option<Method>,
verbatim_doc_comment: Option<Ident>,
next_display_order: Option<Method>,
next_help_heading: Option<Method>,
help_heading: Option<Method>,
is_enum: bool,
has_custom_parser: bool,
Expand Down Expand Up @@ -379,6 +381,8 @@ impl Attrs {
author: None,
version: None,
verbatim_doc_comment: None,
next_display_order: None,
next_help_heading: None,
help_heading: None,
is_enum: false,
has_custom_parser: false,
Expand Down Expand Up @@ -532,9 +536,16 @@ impl Attrs {
self.methods.push(Method::new(raw_ident, val));
}

NextDisplayOrder(ident, expr) => {
self.next_display_order = Some(Method::new(ident, quote!(#expr)));
}

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 +634,14 @@ impl Attrs {

/// generate methods from attributes on top of struct or enum
pub fn initial_top_level_methods(&self) -> TokenStream {
let next_display_order = self.next_display_order.as_ref().into_iter();
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_display_order)*
#(#next_help_heading)*
#(#help_heading)*
)
}

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

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

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
15 changes: 8 additions & 7 deletions clap_derive/src/derives/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,20 +213,21 @@ 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();
let next_display_order = attrs.next_display_order();
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 #next_display_order;
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 #next_display_order;
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
15 changes: 8 additions & 7 deletions clap_derive/src/derives/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,20 +187,21 @@ 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 next_display_order = attrs.next_display_order();
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 #next_display_order;
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 #next_display_order;
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
21 changes: 21 additions & 0 deletions clap_derive/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub enum ClapAttr {
NameExpr(Ident, Expr),
DefaultValueT(Ident, Option<Expr>),
DefaultValueOsT(Ident, Option<Expr>),
NextDisplayOrder(Ident, Expr),
NextHelpHeading(Ident, Expr),
HelpHeading(Ident, Expr),

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

"next_display_order" => {
let expr = ExprLit {
attrs: vec![],
lit: Lit::Str(lit),
};
let expr = Expr::Lit(expr);
Ok(NextDisplayOrder(name, 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 +150,8 @@ 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_display_order" => Ok(NextDisplayOrder(name, expr)),
"next_help_heading" => Ok(NextHelpHeading(name, expr)),
"help_heading" => Ok(HelpHeading(name, expr)),
_ => Ok(NameExpr(name, expr)),
},
Expand Down
3 changes: 2 additions & 1 deletion examples/derive_ref/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ 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_display_order`: `clap::App::next_display_order`
- `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
99 changes: 94 additions & 5 deletions src/build/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use crate::{Error, INTERNAL_ERROR_MSG};
/// // Your program logic starts here...
/// ```
/// [`App::get_matches`]: App::get_matches()
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct App<'help> {
pub(crate) id: Id,
pub(crate) name: String,
Expand Down Expand Up @@ -95,6 +95,7 @@ pub struct App<'help> {
pub(crate) replacers: HashMap<&'help str, &'help [&'help str]>,
pub(crate) groups: Vec<ArgGroup<'help>>,
pub(crate) current_help_heading: Option<&'help str>,
pub(crate) current_disp_ord: Option<usize>,
pub(crate) subcommand_value_name: Option<&'help str>,
pub(crate) subcommand_heading: Option<&'help str>,
}
Expand Down Expand Up @@ -169,6 +170,14 @@ impl<'help> App<'help> {
#[must_use]
pub fn arg<A: Into<Arg<'help>>>(mut self, a: A) -> Self {
let mut arg = a.into();
if let Some(current_disp_ord) = self.current_disp_ord.as_mut() {
if !arg.is_positional() && arg.provider != ArgProvider::Generated {
let current = *current_disp_ord;
arg.disp_ord.set_implicit(current);
*current_disp_ord = current + 1;
}
}

arg.help_heading.get_or_insert(self.current_help_heading);
self.args.push(arg);
self
Expand Down Expand Up @@ -1333,14 +1342,45 @@ 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>>,
{
self.current_help_heading = heading.into();
self
}

/// Change the starting value for assigning future display orders for ags.
///
/// This will be used for any arg that hasn't had [`Arg::display_order`] called.
#[inline]
#[must_use]
pub fn next_display_order(mut self, disp_ord: impl Into<Option<usize>>) -> Self {
self.current_disp_ord = disp_ord.into();
self
}

/// Sets the terminal width at which to wrap help messages.
///
/// Using `0` will ignore terminal widths and use source formatting.
Expand Down Expand Up @@ -2165,7 +2205,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 Expand Up @@ -2993,14 +3042,13 @@ impl<'help> App<'help> {
debug!("App::_derive_display_order:{}", self.name);

if self.settings.is_set(AppSettings::DeriveDisplayOrder) {
for (i, a) in self
for a in self
.args
.args_mut()
.filter(|a| !a.is_positional())
.filter(|a| a.provider != ArgProvider::Generated)
.enumerate()
{
a.disp_ord.get_or_insert(i);
a.disp_ord.make_explicit();
}
for (i, sc) in &mut self.subcommands.iter_mut().enumerate() {
sc.disp_ord.get_or_insert(i);
Expand Down Expand Up @@ -3309,6 +3357,47 @@ impl<'help> App<'help> {
}
}

impl<'help> Default for App<'help> {
fn default() -> Self {
Self {
id: Default::default(),
name: Default::default(),
long_flag: Default::default(),
short_flag: Default::default(),
bin_name: Default::default(),
author: Default::default(),
version: Default::default(),
long_version: Default::default(),
about: Default::default(),
long_about: Default::default(),
before_help: Default::default(),
before_long_help: Default::default(),
after_help: Default::default(),
after_long_help: Default::default(),
aliases: Default::default(),
short_flag_aliases: Default::default(),
long_flag_aliases: Default::default(),
usage_str: Default::default(),
usage: Default::default(),
help_str: Default::default(),
disp_ord: Default::default(),
term_w: Default::default(),
max_w: Default::default(),
template: Default::default(),
settings: Default::default(),
g_settings: Default::default(),
args: Default::default(),
subcommands: Default::default(),
replacers: Default::default(),
groups: Default::default(),
current_help_heading: Default::default(),
current_disp_ord: Some(0),
subcommand_value_name: Default::default(),
subcommand_heading: Default::default(),
}
}
}

impl<'help> Index<&'_ Id> for App<'help> {
type Output = Arg<'help>;

Expand Down
Loading