Repository: clap-rs/clap (language: rust)

Issue:
Subcommand name without hyphen in help
Maintainer's notes
- We hope #1334 will provide a way to resolve this.  This is being left open to ensure this use case isn't lost in case a minimum or full solution doesn't include this
---
<!--
Please use the following template to assist with creating an issue and to ensure a speedy resolution. If an area is not applicable, feel free to delete the area or mark with `N/A`
-->

### Rust Version

* rustc 1.33.0 (2aa4c46cf 2019-02-28)

### Affected Version of clap

* clap 2.32.0

### Bug or Feature Request Summary

Add a flag which supports printing the name of an app subcommand without the hyphen instead of with a hyphen, for apps which do not use external commands.

### Expected Behavior Summary

Clap apps can output `app subcommand` as the name of a subcommand when running `app subcommand --help`.

### Actual Behavior Summary

Clap apps can only output `app-subcommand` as the name of a subcommand when running `app subcommand --help`.

### Steps to Reproduce the issue

1. `App::new('app').subcommand(Subcommand::with_name('subcommand'))`.
2. Run `<appname>

### Sample Code or Link to Sample Code

[notion](https://github.com/notion-cli/notion/pulls) is an app where we want subcommands to print like `notion install` *not* `notion-install`.

### Debug output

Compile clap with cargo features `"debug"` such as:

```toml
[dependencies]
clap = { version = "2", features = ["debug"] }
```

<details>
<summary> Debug Output </summary>
<pre>
<code>

Paste Debug Output Here

</code>
</pre>
</details>



Discussion / hints:
Is there a way to disable this output alltogether?
@CreepySkeleton If I want to do this, I just need to add a new `global_settings` right like `Subcommand::DisableHyphen`?
Unfortunately it's not that simple. We do `app._build()` before parsing which kind of propagates the app binary name to subcommands and the subcommand binary name becomes this. I have been planning to look at the binary name being used in usage messages for quite a while but it's not a priority for v3.
@pksunkara But then it was marked is `D: easy`? I thought it seemed easy but not. T_T

Tests are here:
```rust
static ISSUE_1431: &str = "ctest-foo 

USAGE:
    ctest foo

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information";

#[test] ▶Run Test
fn help_subcommand_without_hyphen() {
    let app = App::new("ctest").subcommand(App::new("foo"));
    assert!(utils::compare_output(
        app,
        "ctest foo -h",
        ISSUE_1431,
        false
    ));
}
```
It took me some time to find the trailing space that fails the test, `ctest-foo `. Especially when I cannot see or copy anything correctly:

<details>
  <summary>Click to expand!</summary>

```
...
failures:

---- help_subcommand_without_hyphen stdout ----

--> left
ctest-foo

USAGE:
    ctest foo

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information
--> right
ctest-foo

USAGE:
    ctest foo

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information
--
thread 'help_subcommand_without_hyphen' panicked at 'assertion failed: utils::compare_output(app, "ctest foo -h", ISSUE_1431, false)', tests/help.rs:1667:5


failures:
    help_subcommand_without_hyphen

test result: FAILED. 59 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
```
</details>
Related to #1382 
My question is why is `-` present and why shouldn't we remove it?

I see someone commented about external subcommands.  I'd be curious to see for that would look like that justifies putting the `-` in.
I think the standard of treating subcommands name is always `maincommand-subcommand`. For example, `git` does that. If you want to read info about `git commit`, you check `man git-commit` etc..

Also, `git` automatically picks up external commands by doing the same. `git-trim` is picked up by `git trim`.

Another aspect from your comment in #1382 is:

> One other aspect of this is that we can have per-subcommand versions.

`app subcommand` doesn't give the impression that it is one command while `app-subcommand` does. And that command is actually the subcommand.
So I've only played with git and docker so far. My original assumption when starting to look at commands was that `git` is a bit unique due to its history of being composed of a lot of a pile of scripts, assuming everything was originally external commands.

Neither mentions the command name, only the usage.  The usage does not use `-`.

The man pages do use `-`.

I'd be interested in exploring other examples and considering whether we should reduce some of the boilerplate, not just whitespace in #3096.  That might also help with help2man.
I think Git is the exception here. With most other tools, the subcommand is not "part" of the main command. `docker image list`, `aws ec2 describe-instances`, `dnf update`. Even with Git, the safest way of accessing the manpage is `git subcommand --help` because that works on systems without `man` (like Windows, where it opens a browser with the docs instead).

Update: On my system, all the git subcommands (i.e. `git-status`) are hidden away, well outside the user's `PATH`. It is difficult (and discouraged) to run the subcommands directly (i.e. `/usr/libexec/git-core/git-status`) and the fact that `git status` runs that binary is an implementation detail and likely not guaranteed. `git status` is the correct incantation, and all of the documentation (including what it displays if you run something like `git status --blargh`) report the command back as `git status [<options>] ...` not `git-status`.
> I think Git is the exception here. With most other tools, the subcommand is not "part" of the main command. `docker image list`, `aws ec2 describe-instances`, `dnf update`. Even with Git, the safest way of accessing the manpage is `git subcommand --help` because that works on systems without `man` (like Windows, where it opens a browser with the docs instead).

docker doesn't show the "name" but `man docker-image`  is used to access the man page.

apt does show the `apt` name but `man apt-get` works.
On thought I had is in https://github.com/clap-rs/clap/issues/1334 we are looking at allowing opting in to flattening subcommand help output, like `git stash -h`.  When flattening, we would only want to show the parent command (`git-stash` in `git stash -h` and `git-stash pop` in `git stash pop -h`).  Trying to decide whether to bake that into #1334 or offer it as a separate flag.  I'm leaning towards baking-in.
Another "second" for subcommand help being a) potentially slightly more configurable and b) defaulting to non-hyphenated. I'm not sure it's user obvious. In the worst case, it could easily be interpreted as an expectation that the user should type `app-subcommand` to invoke, rather than `app subcommand` and while they might get very lucky with oddities like git, there will be no `app-subcommand` binary on their system with this.

Personally, I'd be more than happy if the title at least only ever showed the top-level app name. The actual subcommand is pretty clear from the usage section, as well as any actual help text that's been written.
btw the fix for #1474 will allow users to specify their own display names for commands / subcommands that can be used for help.  Its currently at the phase of explicit opt-in with providing an `help_template` but we plan to make use the display name in the default help

Fix patch:
diff --git a/src/output/help.rs b/src/output/help.rs
index 921261abf79..85ba7d13a29 100644
--- a/src/output/help.rs
+++ b/src/output/help.rs
@@ -32,6 +32,15 @@ pub(crate) struct Help<'help, 'cmd, 'writer> {
 
 // Public Functions
 impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
+    #[cfg(feature = "unstable-v4")]
+    const DEFAULT_TEMPLATE: &'static str = "\
+        {before-help}{name} {version}\n\
+        {author-with-newline}{about-with-newline}\n\
+        {usage-heading}\n    {usage}\n\
+        \n\
+        {all-args}{after-help}\
+    ";
+    #[cfg(not(feature = "unstable-v4"))]
     const DEFAULT_TEMPLATE: &'static str = "\
         {before-help}{bin} {version}\n\
         {author-with-newline}{about-with-newline}\n\
@@ -40,6 +49,13 @@ impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> {
         {all-args}{after-help}\
     ";
 
+    #[cfg(feature = "unstable-v4")]
+    const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\
+        {before-help}{name} {version}\n\
+        {author-with-newline}{about-with-newline}\n\
+        {usage-heading}\n    {usage}{after-help}\
+    ";
+    #[cfg(not(feature = "unstable-v4"))]
     const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\
         {before-help}{bin} {version}\n\
         {author-with-newline}{about-with-newline}\n\


Test patch:
diff --git a/tests/builder/help.rs b/tests/builder/help.rs
index 81859e1807b..7c6332cecbf 100644
--- a/tests/builder/help.rs
+++ b/tests/builder/help.rs
@@ -2615,7 +2615,7 @@ fn override_help_flag_using_short() {
 fn subcommand_help_doesnt_have_useless_help_flag() {
     // The main care-about is that the docs and behavior match.  Since the `help` subcommand
     // currently ignores the `--help` flag, the output shouldn't have it.
-    let cmd = Command::new("test_app").subcommand(Command::new("test").about("Subcommand"));
+    let cmd = Command::new("example").subcommand(Command::new("test").about("Subcommand"));
 
     utils::assert_output(
         cmd,
@@ -2655,7 +2655,7 @@ fn disable_help_flag_affects_help_subcommand() {
 
 #[test]
 fn dont_propagate_version_to_help_subcommand() {
-    let cmd = clap::Command::new("test")
+    let cmd = clap::Command::new("example")
         .version("1.0")
         .propagate_version(true)
         .subcommand(clap::Command::new("subcommand"));