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(mangen): Support flatten_help #5769

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
26 changes: 26 additions & 0 deletions clap_mangen/tests/snapshots/flatten_help.roff
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH my-app 1 "my-app "
.SH NAME
my\-app
.SH SYNOPSIS
\fBmy\-app\fR [\fB\-c \fR] [\fB\-v \fR] [\fB\-h\fR|\fB\-\-help\fR] [\fIsubcommands\fR]
.SH DESCRIPTION
.SH OPTIONS
.TP
\fB\-c\fR

.TP
\fB\-v\fR

.TP
\fB\-h\fR, \fB\-\-help\fR
Print help
.SH SUBCOMMANDS
.TP
my\-app\-test(1)
Subcommand
with a second line
.TP
my\-app\-help(1)
Print this message or the help of the given subcommand(s)
26 changes: 26 additions & 0 deletions clap_mangen/tests/snapshots/flatten_help_subcommand_required.roff
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH my-app 1 "my-app "
.SH NAME
my\-app
.SH SYNOPSIS
\fBmy\-app\fR [\fB\-c \fR] [\fB\-v \fR] [\fB\-h\fR|\fB\-\-help\fR] <\fIsubcommands\fR>
.SH DESCRIPTION
.SH OPTIONS
.TP
\fB\-c\fR

.TP
\fB\-v\fR

.TP
\fB\-h\fR, \fB\-\-help\fR
Print help
.SH SUBCOMMANDS
.TP
my\-app\-test(1)
Subcommand
with a second line
.TP
my\-app\-help(1)
Print this message or the help of the given subcommand(s)
36 changes: 36 additions & 0 deletions clap_mangen/tests/testsuite/roff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,39 @@ fn value_name_without_arg() {
cmd,
);
}

#[test]
fn flatten_help_false() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably cover more cases that help covers, see

clap/tests/builder/help.rs

Lines 3280 to 3971 in 61f5ee5

#[test]
fn flatten_basic() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent test:
test command
--child <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_short_help() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(
Arg::new("parent")
.long("parent")
.help("foo")
.long_help("bar"),
)
.subcommand(
Command::new("test")
.about("test command")
.long_about("long some")
.arg(Arg::new("child").long("child").help("foo").long_help("bar")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent> foo
-h, --help Print help (see more with '--help')
parent test:
test command
--child <child> foo
-h, --help Print help (see more with '--help')
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_long_help() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(
Arg::new("parent")
.long("parent")
.help("foo")
.long_help("bar"),
)
.subcommand(
Command::new("test")
.about("test command")
.long_about("long some")
.arg(Arg::new("child").long("child").help("foo").long_help("bar")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
bar
-h, --help
Print help (see a summary with '-h')
parent test:
test command
--child <child>
bar
-h, --help
Print help (see a summary with '-h')
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]...
Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent --help", expected, false);
}
#[test]
fn flatten_help_cmd() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(
Arg::new("parent")
.long("parent")
.help("foo")
.long_help("bar"),
)
.subcommand(
Command::new("test")
.about("test command")
.long_about("long some")
.arg(Arg::new("child").long("child").help("foo").long_help("bar")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
bar
-h, --help
Print help (see a summary with '-h')
parent test:
test command
--child <child>
bar
-h, --help
Print help (see a summary with '-h')
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]...
Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent help", expected, false);
}
#[test]
fn flatten_with_global() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent").global(true))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent test:
test command
--child <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_arg_required() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent").required(true))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child").required(true)),
);
let expected = str![[r#"
parent command
Usage: parent --parent <parent>
parent --parent <parent> test --child <child>
parent --parent <parent> help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent --parent <parent> test:
test command
--child <child>
-h, --help Print help
parent --parent <parent> help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_with_external_subcommand() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.allow_external_subcommands(true)
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent test:
test command
--child <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_without_subcommands() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"));
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
Options:
--parent <parent>
-h, --help Print help
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_with_subcommand_required() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.subcommand_required(true)
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child")),
);
let expected = str![[r#"
parent command
Usage: parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent test:
test command
--child <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_with_args_conflicts_with_subcommands() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.subcommand_required(true)
.args_conflicts_with_subcommands(true)
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("test")
.about("test command")
.arg(Arg::new("child").long("child")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent test [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent test:
test command
--child <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_single_hidden_command() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("child1")
.hide(true)
.about("child1 command")
.arg(Arg::new("child").long("child1")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
Options:
--parent <parent>
-h, --help Print help
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_hidden_command() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("child1")
.about("child1 command")
.arg(Arg::new("child").long("child1")),
)
.subcommand(
Command::new("child2")
.about("child2 command")
.arg(Arg::new("child").long("child2")),
)
.subcommand(
Command::new("child3")
.hide(true)
.about("child3 command")
.arg(Arg::new("child").long("child3")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent child1 [OPTIONS]
parent child2 [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent child1:
child1 command
--child1 <child>
-h, --help Print help
parent child2:
child2 command
--child2 <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_recursive() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("child1")
.flatten_help(true)
.about("child1 command")
.arg(Arg::new("child").long("child1"))
.subcommand(
Command::new("grandchild1")
.flatten_help(true)
.about("grandchild1 command")
.arg(Arg::new("grandchild").long("grandchild1"))
.subcommand(
Command::new("greatgrandchild1")
.about("greatgrandchild1 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild1")),
)
.subcommand(
Command::new("greatgrandchild2")
.about("greatgrandchild2 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild2")),
)
.subcommand(
Command::new("greatgrandchild3")
.about("greatgrandchild3 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild3")),
),
)
.subcommand(
Command::new("grandchild2")
.about("grandchild2 command")
.arg(Arg::new("grandchild").long("grandchild2")),
)
.subcommand(
Command::new("grandchild3")
.about("grandchild3 command")
.arg(Arg::new("grandchild").long("grandchild3")),
),
)
.subcommand(
Command::new("child2")
.about("child2 command")
.arg(Arg::new("child").long("child2")),
)
.subcommand(
Command::new("child3")
.hide(true)
.about("child3 command")
.arg(Arg::new("child").long("child3"))
.subcommand(
Command::new("grandchild1")
.flatten_help(true)
.about("grandchild1 command")
.arg(Arg::new("grandchild").long("grandchild1"))
.subcommand(
Command::new("greatgrandchild1")
.about("greatgrandchild1 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild1")),
)
.subcommand(
Command::new("greatgrandchild2")
.about("greatgrandchild2 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild2")),
)
.subcommand(
Command::new("greatgrandchild3")
.about("greatgrandchild3 command")
.arg(Arg::new("greatgrandchild").long("greatgrandchild3")),
),
)
.subcommand(
Command::new("grandchild2")
.about("grandchild2 command")
.arg(Arg::new("grandchild").long("grandchild2")),
)
.subcommand(
Command::new("grandchild3")
.about("grandchild3 command")
.arg(Arg::new("grandchild").long("grandchild3")),
),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent child1 [OPTIONS]
parent child1 grandchild1 [OPTIONS]
parent child1 grandchild1 greatgrandchild1 [OPTIONS]
parent child1 grandchild1 greatgrandchild2 [OPTIONS]
parent child1 grandchild1 greatgrandchild3 [OPTIONS]
parent child1 grandchild1 help [COMMAND]
parent child1 grandchild2 [OPTIONS]
parent child1 grandchild3 [OPTIONS]
parent child1 help [COMMAND]
parent child2 [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent child1:
child1 command
--child1 <child>
-h, --help Print help
parent child1 grandchild1:
grandchild1 command
--grandchild1 <grandchild>
-h, --help Print help
parent child1 grandchild1 greatgrandchild1:
greatgrandchild1 command
--greatgrandchild1 <greatgrandchild>
-h, --help Print help
parent child1 grandchild1 greatgrandchild2:
greatgrandchild2 command
--greatgrandchild2 <greatgrandchild>
-h, --help Print help
parent child1 grandchild1 greatgrandchild3:
greatgrandchild3 command
--greatgrandchild3 <greatgrandchild>
-h, --help Print help
parent child1 grandchild1 help:
Print this message or the help of the given subcommand(s)
parent child1 grandchild2:
grandchild2 command
--grandchild2 <grandchild>
-h, --help Print help
parent child1 grandchild3:
grandchild3 command
--grandchild3 <grandchild>
-h, --help Print help
parent child1 help:
Print this message or the help of the given subcommand(s)
parent child2:
child2 command
--child2 <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}
#[test]
fn flatten_not_recursive() {
let cmd = Command::new("parent")
.flatten_help(true)
.about("parent command")
.arg(Arg::new("parent").long("parent"))
.subcommand(
Command::new("child1")
.about("child1 command")
.arg(Arg::new("child").long("child1"))
.subcommand(
Command::new("grandchild1")
.about("grandchild1 command")
.arg(Arg::new("grandchild").long("grandchild1")),
)
.subcommand(
Command::new("grandchild2")
.about("grandchild2 command")
.arg(Arg::new("grandchild").long("grandchild2")),
)
.subcommand(
Command::new("grandchild3")
.about("grandchild3 command")
.arg(Arg::new("grandchild").long("grandchild3")),
),
)
.subcommand(
Command::new("child2")
.about("child2 command")
.arg(Arg::new("child").long("child2")),
)
.subcommand(
Command::new("child3")
.about("child3 command")
.arg(Arg::new("child").long("child3")),
);
let expected = str![[r#"
parent command
Usage: parent [OPTIONS]
parent child1 [OPTIONS] [COMMAND]
parent child2 [OPTIONS]
parent child3 [OPTIONS]
parent help [COMMAND]...
Options:
--parent <parent>
-h, --help Print help
parent child1:
child1 command
--child1 <child>
-h, --help Print help
parent child2:
child2 command
--child2 <child>
-h, --help Print help
parent child3:
child3 command
--child3 <child>
-h, --help Print help
parent help:
Print this message or the help of the given subcommand(s)
[COMMAND]... Print help for the subcommand(s)
"#]];
utils::assert_output(cmd, "parent -h", expected, false);
}

e.g.

  • we don't need to cover short vs long
  • we should cover recursion

It'd be a big help to reuse the same commands as those tests so we can easily compare the help output vs man output

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added all relevant tests from help.rs and at least the recursive stuff isn't working right now. Is the recursive stuff strictly required for this PR to move forward?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive behavior is part of the definition of flatten_help. We could possibly split it out but we'd need to be tracking it somehow. Its likely best to go ahead and do that as it might affect the design in important ways

let name = "my-app";
let cmd = common::basic_command(name).flatten_help(false);
common::assert_matches(snapbox::file!["../snapshots/basic.bash.roff"], cmd);
}

#[test]
fn flatten_help_true() {
let name = "my-app";
let cmd = common::basic_command(name).flatten_help(true);
common::assert_matches(snapbox::file!["../snapshots/flatten_help.roff"], cmd);
}

#[test]
fn flatten_help_true_subcommand_required_true() {
let name = "my-app";
let cmd = common::basic_command(name)
.flatten_help(true)
.subcommand_required(true);
common::assert_matches(
snapbox::file!["../snapshots/flatten_help_subcommand_required.roff"],
cmd,
);
}

#[test]
fn flatten_help_true_subcommand_args_conflicts_with_subcommands() {
Comment on lines +117 to +137
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these tests still needed with the addition of the --help tests?

let name = "my-app";
let cmd = common::basic_command(name)
.flatten_help(true)
.subcommand_required(false)
.args_conflicts_with_subcommands(false);
common::assert_matches(snapbox::file!["../snapshots/flatten_help.roff"], cmd);
}
epage marked this conversation as resolved.
Show resolved Hide resolved