fix(derive): Allow 'long_help' to force populating from doc comment

Fixes #4441
This commit is contained in:
Ed Page 2022-11-07 09:41:43 -06:00
parent 8751152316
commit c37ab6c205
5 changed files with 42 additions and 11 deletions

View file

@ -105,6 +105,8 @@ impl Parse for ClapAttr {
"external_subcommand" => Some(MagicAttrName::ExternalSubcommand),
"verbatim_doc_comment" => Some(MagicAttrName::VerbatimDocComment),
"about" => Some(MagicAttrName::About),
"long_about" => Some(MagicAttrName::LongAbout),
"long_help" => Some(MagicAttrName::LongHelp),
"author" => Some(MagicAttrName::Author),
"version" => Some(MagicAttrName::Version),
_ => None,
@ -160,6 +162,8 @@ pub enum MagicAttrName {
VerbatimDocComment,
ExternalSubcommand,
About,
LongAbout,
LongHelp,
Author,
Version,
RenameAllEnv,

View file

@ -43,6 +43,7 @@ pub struct Item {
value_parser: Option<ValueParser>,
action: Option<Action>,
verbatim_doc_comment: bool,
force_long_help: bool,
next_display_order: Option<Method>,
next_help_heading: Option<Method>,
is_enum: bool,
@ -266,6 +267,7 @@ impl Item {
value_parser: None,
action: None,
verbatim_doc_comment: false,
force_long_help: false,
next_display_order: None,
next_help_heading: None,
is_enum: false,
@ -504,6 +506,18 @@ impl Item {
}
}
Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
self.force_long_help = true;
}
Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
self.force_long_help = true;
}
Some(MagicAttrName::Author) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
@ -820,6 +834,8 @@ impl Item {
| Some(MagicAttrName::Long)
| Some(MagicAttrName::Env)
| Some(MagicAttrName::About)
| Some(MagicAttrName::LongAbout)
| Some(MagicAttrName::LongHelp)
| Some(MagicAttrName::Author)
| Some(MagicAttrName::Version)
=> {
@ -873,7 +889,8 @@ impl Item {
let lines = extract_doc_comment(attrs);
if !lines.is_empty() {
let (short_help, long_help) = format_doc_comment(&lines, !self.verbatim_doc_comment);
let (short_help, long_help) =
format_doc_comment(&lines, !self.verbatim_doc_comment, self.force_long_help);
let short_name = format_ident!("{}", short_name);
let short = Method::new(
short_name,

View file

@ -47,7 +47,11 @@ pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> {
lines
}
pub fn format_doc_comment(lines: &[String], preprocess: bool) -> (Option<String>, Option<String>) {
pub fn format_doc_comment(
lines: &[String],
preprocess: bool,
force_long: bool,
) -> (Option<String>, Option<String>) {
if let Some(first_blank) = lines.iter().position(|s| is_blank(s)) {
let (short, long) = if preprocess {
let paragraphs = split_paragraphs(lines);
@ -62,14 +66,18 @@ pub fn format_doc_comment(lines: &[String], preprocess: bool) -> (Option<String>
(Some(short), Some(long))
} else {
let short = if preprocess {
let s = merge_lines(lines);
remove_period(s)
let (short, long) = if preprocess {
let short = merge_lines(lines);
let long = force_long.then(|| short.clone());
let short = remove_period(short);
(short, long)
} else {
lines.join("\n")
let short = lines.join("\n");
let long = force_long.then(|| short.clone());
(short, long)
};
(Some(short), None)
(Some(short), long)
}
}

View file

@ -154,8 +154,9 @@
//! - **TIP:** When a doc comment is also present, you most likely want to add
//! `#[arg(long_about = None)]` to clear the doc comment so only [`about`][crate::Command::about]
//! gets shown with both `-h` and `--help`.
//! - `long_about = <expr>`: [`Command::long_about`][crate::Command::long_about]
//! - `long_about[ = <expr>]`: [`Command::long_about`][crate::Command::long_about]
//! - When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
//! - When present without a value: [Doc comment](#doc-comments)
//! - `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to [`about`][crate::Command::about] / [`long_about`][crate::Command::long_about]
//! - `next_display_order`: [`Command::next_display_order`][crate::Command::next_display_order]
//! - `next_help_heading`: [`Command::next_help_heading`][crate::Command::next_help_heading]
@ -200,8 +201,9 @@
//! - When not present: will auto-select an action based on the field type
//! - `help = <expr>`: [`Arg::help`][crate::Arg::help]
//! - When not present: [Doc comment summary](#doc-comments)
//! - `long_help = <expr>`: [`Arg::long_help`][crate::Arg::long_help]
//! - `long_help[ = <expr>]`: [`Arg::long_help`][crate::Arg::long_help]
//! - When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing
//! - When present without a value: [Doc comment](#doc-comments)
//! - `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to [`help`][crate::Arg::help] / [`long_help`][crate::Arg::long_help]
//! - `short [= <char>]`: [`Arg::short`][crate::Arg::short]
//! - When not present: no short set

View file

@ -296,10 +296,10 @@ fn force_long_help() {
struct LoremIpsum {
/// Fooify a bar
/// and a baz.
#[arg(short, long)]
#[arg(short, long, long_help)]
foo: bool,
}
let help = utils::get_long_help::<LoremIpsum>();
assert!(help.contains("Fooify a bar and a baz"));
assert!(help.contains("Fooify a bar and a baz."));
}