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

Issue:
[clap_mangen] possible values not shown in generated man page
### Please complete the following tasks

- [X] I have searched the [discussions](https://github.com/clap-rs/clap/discussions)
- [X] I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues

### Rust Version

rustc 1.61.0 (fe5b13d68 2022-05-18)

### Clap Version

3.2.6

### Minimal reproducible code

```rust
use clap::{Parser, CommandFactory};
use clap_mangen::Man;

#[derive(Parser)]
struct Cli {
    #[clap(long, action)]
    pub test: Option<bool>,
}

fn main() -> std::io::Result<()> {
    let mut cmd = Cli::command();

    cmd.write_long_help(&mut std::io::stderr())?;
    eprintln!("{}", "-".repeat(30));

    let man = Man::new(cmd);
    let mut buffer: Vec<u8> = Default::default();
    man.render(&mut buffer)?;
    println!("{}", String::from_utf8_lossy(&buffer));

    Ok(())
}
```

### Steps to reproduce the bug with the above code

1. Add `clap = { version = "3.2.6", features = ["derive"] }` and `clap_mangen = "0.1.9"` to `Cargo.toml`
2. Run `cargo run | man -P cat -l -`  

### Actual Behaviour

The above example prints both the normal help message generated by `clap` and the man page generated by `clap_mangen` (example output is provided below). The man page does not mention the possible values (`true` and `false`) anywhere. The same is true for enums.

### Expected Behaviour

The man page should mention the possible values just like the help message does (`[possible values: true, false]`).

### Additional Context

Output of above example:
```
asd

USAGE:
    asd [OPTIONS]

OPTIONS:
    -h, --help
            Print help information

        --test <TEST>
            [possible values: true, false]
------------------------------
asd(1)      General Commands Manual      asd(1)

NAME
       asd

SYNOPSIS
       asd [-h|--help] [--test]

DESCRIPTION
OPTIONS
       -h, --help
              Print help information

       --test=TEST

                      asd                asd(1)
```

### Debug Output

```
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `/home/silas/.cache/cargo/debug/asd`
[      clap::builder::command] 	Command::_build: name="asd"
[      clap::builder::command] 	Command::_propagate:asd
[      clap::builder::command] 	Command::_check_help_and_version: asd
[      clap::builder::command] 	Command::_check_help_and_version: Removing generated version
[      clap::builder::command] 	Command::_propagate_global_args:asd
[      clap::builder::command] 	Command::_derive_display_order:asd
[clap::builder::debug_asserts] 	Command::_debug_asserts
[clap::builder::debug_asserts] 	Arg::_debug_asserts:help
[clap::builder::debug_asserts] 	Arg::_debug_asserts:test
[clap::builder::debug_asserts] 	Command::_verify_positionals
[          clap::output::help] 	Help::new cmd=asd, use_long=true
[          clap::output::help] 	Help::write_help
[          clap::output::help] 	should_show_arg: use_long=true, arg=help
[          clap::output::help] 	Help::write_templated_help
[          clap::output::help] 	Help::write_before_help
[          clap::output::help] 	Help::write_bin_name
asd 

USAGE:
    [         clap::output::usage] 	Usage::create_usage_no_title
[         clap::output::usage] 	Usage::create_help_usage; incl_reqs=true
[         clap::output::usage] 	Usage::get_required_usage_from: incls=[], matcher=false, incl_last=false
[         clap::output::usage] 	Usage::get_required_usage_from: unrolled_reqs={}
[         clap::output::usage] 	Usage::get_required_usage_from: ret_val={}
[         clap::output::usage] 	Usage::needs_options_tag
[         clap::output::usage] 	Usage::needs_options_tag:iter: f=help
[         clap::output::usage] 	Usage::needs_options_tag:iter Option is built-in
[         clap::output::usage] 	Usage::needs_options_tag:iter: f=test
[      clap::builder::command] 	Command::groups_for_arg: id=test
[         clap::output::usage] 	Usage::needs_options_tag:iter: [OPTIONS] required
[         clap::output::usage] 	Usage::create_help_usage: usage=asd [OPTIONS]
asd [OPTIONS]

[          clap::output::help] 	Help::write_all_args
[          clap::output::help] 	should_show_arg: use_long=true, arg=help
[          clap::output::help] 	should_show_arg: use_long=true, arg=test
OPTIONS:
[          clap::output::help] 	Help::write_args OPTIONS
[          clap::output::help] 	should_show_arg: use_long=true, arg=help
[          clap::output::help] 	Help::write_args: arg="help" longest=6
[          clap::output::help] 	should_show_arg: use_long=true, arg=test
[          clap::output::help] 	Help::write_args: arg="test" longest=13
[          clap::output::help] 	should_show_arg: use_long=true, arg=help
[          clap::output::help] 	Help::spec_vals: a=--help
[          clap::output::help] 	Help::spec_vals: a=--help
[          clap::output::help] 	Help::short
    -h[          clap::output::help] 	Help::long
, --help[          clap::output::help] 	Help::val: arg=help
[          clap::output::help] 	Help::align_to_about: arg=help, next_line_help=true, longest=13
[          clap::output::help] 	Help::align_to_about: printing long help so skip alignment
[          clap::output::help] 	Help::help
[          clap::output::help] 	Help::help: Next Line...true

            [          clap::output::help] 	Help::help: Too long...
[          clap::output::help] 	No
Print help information

[          clap::output::help] 	Help::spec_vals: a=--test <TEST>
[          clap::output::help] 	Help::spec_vals: Found possible vals...[PossibleValue { name: "true", help: None, aliases: [], hide: false }, PossibleValue { name: "false", help: None, aliases: [], hide: false }]
[          clap::output::help] 	Help::short
        [          clap::output::help] 	Help::long
--test[          clap::output::help] 	Help::val: arg=test
 <TEST>[          clap::output::help] 	Help::align_to_about: arg=test, next_line_help=true, longest=13
[          clap::output::help] 	Help::align_to_about: printing long help so skip alignment
[          clap::output::help] 	Help::help
[          clap::output::help] 	Help::help: Next Line...true

            [          clap::output::help] 	Help::help: Too long...
[          clap::output::help] 	No
[possible values: true, false][          clap::output::help] 	Help::write_after_help

------------------------------
[      clap::builder::command] 	Command::_build: name="asd"
[      clap::builder::command] 	Command::_build: already built
[      clap::builder::command] 	Command::_build_bin_names
[         clap::output::usage] 	Usage::get_required_usage_from: incls=[], matcher=false, incl_last=true
[         clap::output::usage] 	Usage::get_required_usage_from: unrolled_reqs={}
[         clap::output::usage] 	Usage::get_required_usage_from: ret_val={}
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH asd 1  "asd " 
.SH NAME
asd
.SH SYNOPSIS
\fBasd\fR [\fB\-h\fR|\fB\-\-help\fR] [\fB\-\-test\fR] 
.SH DESCRIPTION
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help information
.TP
\fB\-\-test\fR=\fITEST\fR



```


Discussion / hints:
Huh, we have #3360 for improving possible values but not an issue for tracking showing them in the first place.
I wouldn't mind working on this if it's not being handled already
Go for it!

Fix patch:
diff --git a/clap_mangen/src/render.rs b/clap_mangen/src/render.rs
index b17f3bb849e..60775fa9dc3 100644
--- a/clap_mangen/src/render.rs
+++ b/clap_mangen/src/render.rs
@@ -1,4 +1,5 @@
 use clap::AppSettings;
+
 use roff::{bold, italic, roman, Inline, Roff};
 
 pub(crate) fn subcommand_heading(cmd: &clap::Command) -> String {
@@ -108,7 +109,9 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
         }
 
         let mut body = vec![];
+        let mut arg_help_written = false;
         if let Some(help) = opt.get_long_help().or_else(|| opt.get_help()) {
+            arg_help_written = true;
             body.push(roman(help));
         }
 
@@ -116,6 +119,35 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
         roff.text(header);
         roff.text(body);
 
+        if let Some((possible_values_text, with_help)) = get_possible_values(opt) {
+            if arg_help_written {
+                // It looks nice to have a separation between the help and the values
+                roff.text([Inline::LineBreak]);
+            }
+            if with_help {
+                roff.text([Inline::LineBreak, italic("Possible values:")]);
+
+                // Need to indent twice to get it to look right, because .TP heading indents, but
+                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
+                // size is 7 for terminal devices
+                roff.control("RS", ["14"]);
+                for line in possible_values_text {
+                    roff.control("IP", ["\\(bu", "2"]);
+                    roff.text([roman(line)]);
+                }
+                roff.control("RE", []);
+            } else {
+                let possible_value_text: Vec<Inline> = vec![
+                    Inline::LineBreak,
+                    roman("["),
+                    italic("possible values: "),
+                    roman(possible_values_text.join(", ")),
+                    roman("]"),
+                ];
+                roff.text(possible_value_text);
+            }
+        }
+
         if let Some(env) = option_environment(opt) {
             roff.control("RS", []);
             roff.text(env);
@@ -139,8 +171,10 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
         }
 
         let mut body = vec![];
+        let mut arg_help_written = false;
         if let Some(help) = pos.get_long_help().or_else(|| pos.get_help()) {
             body.push(roman(&help.to_string()));
+            arg_help_written = true;
         }
 
         roff.control("TP", []);
@@ -152,6 +186,35 @@ pub(crate) fn options(roff: &mut Roff, cmd: &clap::Command) {
             roff.text(env);
             roff.control("RE", []);
         }
+        // If possible options are available
+        if let Some((possible_values_text, with_help)) = get_possible_values(pos) {
+            if arg_help_written {
+                // It looks nice to have a separation between the help and the values
+                roff.text([Inline::LineBreak]);
+            }
+            if with_help {
+                roff.text([Inline::LineBreak, italic("Possible values:")]);
+
+                // Need to indent twice to get it to look right, because .TP heading indents, but
+                // that indent doesn't Carry over to the .IP for the bullets. The standard shift
+                // size is 7 for terminal devices
+                roff.control("RS", ["14"]);
+                for line in possible_values_text {
+                    roff.control("IP", ["\\(bu", "2"]);
+                    roff.text([roman(line)]);
+                }
+                roff.control("RE", []);
+            } else {
+                let possible_value_text: Vec<Inline> = vec![
+                    Inline::LineBreak,
+                    roman("["),
+                    italic("possible values: "),
+                    roman(possible_values_text.join(", ")),
+                    roman("]"),
+                ];
+                roff.text(possible_value_text);
+            }
+        }
     }
 }
 
@@ -240,3 +303,35 @@ fn option_default_values(opt: &clap::Arg) -> Option<String> {
 
     None
 }
+
+fn get_possible_values(arg: &clap::Arg) -> Option<(Vec<String>, bool)> {
+    let possibles = arg.get_value_parser().possible_values();
+
+    if let Some(possible_vals) = possibles {
+        let possibles: Vec<_> = possible_vals.collect();
+        let shown_possibles: Vec<_> = possibles.iter().filter(|pos| !pos.is_hide_set()).collect();
+
+        if !(shown_possibles.is_empty() || arg.is_hide_possible_values_set()) {
+            return Some(format_possible_values(&shown_possibles));
+        }
+        return None;
+    }
+    None
+}
+
+fn format_possible_values(possibles: &[&clap::builder::PossibleValue]) -> (Vec<String>, bool) {
+    let mut lines = vec![];
+    let with_help = possibles.iter().any(|p| p.get_help().is_some());
+    if with_help {
+        for value in possibles {
+            let val_name = value.get_name();
+            match value.get_help() {
+                Some(help) => lines.push(format!("{}: {}", val_name, help)),
+                None => lines.push(val_name.to_string()),
+            }
+        }
+    } else {
+        lines.append(&mut possibles.iter().map(|p| p.get_name().to_string()).collect());
+    }
+    (lines, with_help)
+}


Test patch:
diff --git a/clap_mangen/tests/common.rs b/clap_mangen/tests/common.rs
index 3b8bcdc3cfb..836f9c5a69e 100644
--- a/clap_mangen/tests/common.rs
+++ b/clap_mangen/tests/common.rs
@@ -1,3 +1,5 @@
+use clap::builder::PossibleValue;
+
 pub fn basic_command(name: &'static str) -> clap::Command<'static> {
     clap::Command::new(name)
         .arg(clap::Arg::new("config").short('c').global(true))
@@ -252,3 +254,36 @@ pub fn assert_matches_path(expected_path: impl AsRef<std::path::Path>, cmd: clap
         .action_env("SNAPSHOTS")
         .matches_path(expected_path, buf);
 }
+
+pub fn possible_values_command(name: &'static str) -> clap::Command {
+    clap::Command::new(name)
+        .trailing_var_arg(true)
+        .arg(
+            clap::Arg::new("choice")
+                .long("choice")
+                .action(clap::ArgAction::Set)
+                .value_parser(["bash", "fish", "zsh"]),
+        )
+        .arg(
+            clap::Arg::new("method")
+                .long("method")
+                .action(clap::ArgAction::Set)
+                .value_parser([
+                    PossibleValue::new("fast").help("use the Fast method"),
+                    PossibleValue::new("slow").help("use the slow method"),
+                    PossibleValue::new("normal")
+                        .help("use normal mode")
+                        .hide(true),
+                ]),
+        )
+        .arg(
+            clap::Arg::new("positional_choice")
+                .action(clap::ArgAction::Set)
+                .help("Pick the Position you want the command to run in")
+                .value_parser([
+                    PossibleValue::new("left").help("run left adjusted"),
+                    PossibleValue::new("right"),
+                    PossibleValue::new("center").hide(true),
+                ]),
+        )
+}
diff --git a/clap_mangen/tests/roff.rs b/clap_mangen/tests/roff.rs
index 946b8e764e5..0c6d99dbf33 100644
--- a/clap_mangen/tests/roff.rs
+++ b/clap_mangen/tests/roff.rs
@@ -62,3 +62,10 @@ fn value_env() {
     let cmd = common::env_value_command(name);
     common::assert_matches_path("tests/snapshots/value_env.bash.roff", cmd);
 }
+
+#[test]
+fn possible_values() {
+    let name = "my-app";
+    let cmd = common::possible_values_command(name);
+    common::assert_matches_path("tests/snapshots/possible_values.bash.roff", cmd);
+}
diff --git a/clap_mangen/tests/snapshots/feature_sample.bash.roff b/clap_mangen/tests/snapshots/feature_sample.bash.roff
index ae9be971f3a..4c43af3f5a5 100644
--- a/clap_mangen/tests/snapshots/feature_sample.bash.roff
+++ b/clap_mangen/tests/snapshots/feature_sample.bash.roff
@@ -23,6 +23,8 @@ some input file
 .TP
 [/fIchoice/fR]
 
+.br
+[/fIpossible values: /fRfirst, second]
 .SH SUBCOMMANDS
 .TP
 my/-app/-test(1)
diff --git a/clap_mangen/tests/snapshots/possible_values.bash.roff b/clap_mangen/tests/snapshots/possible_values.bash.roff
new file mode 100644
index 00000000000..b150a540f34
--- /dev/null
+++ b/clap_mangen/tests/snapshots/possible_values.bash.roff
@@ -0,0 +1,41 @@
+.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/-h/fR|/fB/-/-help/fR] [/fB/-/-choice/fR] [/fB/-/-method/fR] [/fIpositional_choice/fR] 
+.SH DESCRIPTION
+.SH OPTIONS
+.TP
+/fB/-h/fR, /fB/-/-help/fR
+Print help information
+.TP
+/fB/-/-choice/fR
+
+.br
+[/fIpossible values: /fRbash, fish, zsh]
+.TP
+/fB/-/-method/fR
+
+.br
+/fIPossible values:/fR
+.RS 14
+.IP /(bu 2
+fast: use the Fast method
+.IP /(bu 2
+slow: use the slow method
+.RE
+.TP
+[/fIpositional_choice/fR]
+Pick the Position you want the command to run in
+.br
+
+.br
+/fIPossible values:/fR
+.RS 14
+.IP /(bu 2
+left: run left adjusted
+.IP /(bu 2
+right
+.RE
diff --git a/clap_mangen/tests/snapshots/special_commands.bash.roff b/clap_mangen/tests/snapshots/special_commands.bash.roff
index 08d364d8bc3..47c1c473b43 100644
--- a/clap_mangen/tests/snapshots/special_commands.bash.roff
+++ b/clap_mangen/tests/snapshots/special_commands.bash.roff
@@ -23,6 +23,8 @@ some input file
 .TP
 [/fIchoice/fR]
 
+.br
+[/fIpossible values: /fRfirst, second]
 .SH SUBCOMMANDS
 .TP
 my/-app/-test(1)
diff --git a/clap_mangen/tests/snapshots/sub_subcommands.bash.roff b/clap_mangen/tests/snapshots/sub_subcommands.bash.roff
index e364240fe71..f23a250285d 100644
--- a/clap_mangen/tests/snapshots/sub_subcommands.bash.roff
+++ b/clap_mangen/tests/snapshots/sub_subcommands.bash.roff
@@ -23,6 +23,8 @@ some input file
 .TP
 [/fIchoice/fR]
 
+.br
+[/fIpossible values: /fRfirst, second]
 .SH SUBCOMMANDS
 .TP
 my/-app/-test(1)
diff --git a/clap_mangen/tests/snapshots/value_hint.bash.roff b/clap_mangen/tests/snapshots/value_hint.bash.roff
index cb02d3ca96f..3ff1f76e1a7 100644
--- a/clap_mangen/tests/snapshots/value_hint.bash.roff
+++ b/clap_mangen/tests/snapshots/value_hint.bash.roff
@@ -13,6 +13,8 @@ Print help information
 .TP
 /fB/-/-choice/fR
 
+.br
+[/fIpossible values: /fRbash, fish, zsh]
 .TP
 /fB/-/-unknown/fR