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

Backport Bugfix/3861 to V3: Mangen not showing possible values #4146

Merged
merged 4 commits into from Aug 31, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 95 additions & 0 deletions 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 {
Expand Down Expand Up @@ -108,14 +109,45 @@ 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));
}

roff.control("TP", []);
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);
Expand All @@ -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", []);
Expand All @@ -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);
}
}
}
}

Expand Down Expand Up @@ -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)
}
35 changes: 35 additions & 0 deletions 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))
Expand Down Expand Up @@ -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),
]),
)
}
7 changes: 7 additions & 0 deletions clap_mangen/tests/roff.rs
Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions clap_mangen/tests/snapshots/feature_sample.bash.roff
Expand Up @@ -23,6 +23,8 @@ some input file
.TP
[/fIchoice/fR]

.br
[/fIpossible values: /fRfirst, second]
.SH SUBCOMMANDS
.TP
my/-app/-test(1)
Expand Down
41 changes: 41 additions & 0 deletions 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
2 changes: 2 additions & 0 deletions clap_mangen/tests/snapshots/special_commands.bash.roff
Expand Up @@ -23,6 +23,8 @@ some input file
.TP
[/fIchoice/fR]

.br
[/fIpossible values: /fRfirst, second]
.SH SUBCOMMANDS
.TP
my/-app/-test(1)
Expand Down
2 changes: 2 additions & 0 deletions clap_mangen/tests/snapshots/sub_subcommands.bash.roff
Expand Up @@ -23,6 +23,8 @@ some input file
.TP
[/fIchoice/fR]

.br
[/fIpossible values: /fRfirst, second]
.SH SUBCOMMANDS
.TP
my/-app/-test(1)
Expand Down
2 changes: 2 additions & 0 deletions clap_mangen/tests/snapshots/value_hint.bash.roff
Expand Up @@ -13,6 +13,8 @@ Print help information
.TP
/fB/-/-choice/fR

.br
[/fIpossible values: /fRbash, fish, zsh]
.TP
/fB/-/-unknown/fR

Expand Down