From 13b02a576664936a92b71872112dd169eb076cd5 Mon Sep 17 00:00:00 2001 From: Jeff Dickey Date: Tue, 23 Jun 2026 13:29:45 -0500 Subject: [PATCH] fix(docs): show negated flags in cli help --- lib/src/docs/cli/mod.rs | 26 +++++++++++++++++++ .../cli/templates/spec_template_long.tera | 4 +-- .../cli/templates/spec_template_short.tera | 2 +- lib/src/docs/models.rs | 17 +++++++----- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/src/docs/cli/mod.rs b/lib/src/docs/cli/mod.rs index 9933fcdf..3c34b553 100644 --- a/lib/src/docs/cli/mod.rs +++ b/lib/src/docs/cli/mod.rs @@ -113,6 +113,32 @@ arg "[default]" help="Arg with default value" default="default value" "); } + #[test] + fn test_render_help_with_negated_flag() { + let spec = crate::spec! { r#" +bin "testcli" +flag "--compress" negate="--no-compress" default=#true help="Compress output" +flag "--verbose" help="Verbose output" + "# } + .unwrap(); + + assert_snapshot!(render_help(&spec, &spec.cmd, false), @r" + Usage: testcli [--compress] [--verbose] + + Flags: + --compress / --no-compress Compress output + --verbose Verbose output + "); + + assert_snapshot!(render_help(&spec, &spec.cmd, true), @r" + Usage: testcli [--compress] [--verbose] + + Flags: + --compress / --no-compress Compress output + --verbose Verbose output + "); + } + #[test] fn test_render_help_with_before_after_help() { let spec = crate::spec! { r#" diff --git a/lib/src/docs/cli/templates/spec_template_long.tera b/lib/src/docs/cli/templates/spec_template_long.tera index 6115d52e..31aa48ae 100644 --- a/lib/src/docs/cli/templates/spec_template_long.tera +++ b/lib/src/docs/cli/templates/spec_template_long.tera @@ -69,12 +69,12 @@ Arguments: Flags: {%- for flag in cmd.flags %} {%- if flag.help_rendered %} - {{ flag.usage | ljust(width=flag.usage_col_width) }} {{ flag.help_rendered }} + {{ flag.display_usage | ljust(width=flag.usage_col_width) }} {{ flag.help_rendered }} {%- if flag.help_is_multiline %} {%- endif %} {%- else %} - {{ flag.usage | trim }} + {{ flag.display_usage | trim }} {%- if flag.aliases %} [aliases: {{ flag.aliases | join(sep=", ") }}]{% endif %} {%- set help = flag.help_long | default(value=flag.help | default(value='')) %} {%- if help %} diff --git a/lib/src/docs/cli/templates/spec_template_short.tera b/lib/src/docs/cli/templates/spec_template_short.tera index 5eb21e01..b1e45fa3 100644 --- a/lib/src/docs/cli/templates/spec_template_short.tera +++ b/lib/src/docs/cli/templates/spec_template_short.tera @@ -40,7 +40,7 @@ Arguments: Flags: {%- for flag in cmd.flags %} - {{ flag.usage | trim }} + {{ flag.display_usage | trim }} {%- if flag.aliases %} [aliases: {{ flag.aliases | join(sep=", ") }}]{% endif %} {%- if flag.help %} {{ flag.help }}{%- endif %} {%- if flag.arg.choices and flag.arg.choices.choices %} [{{ flag.arg.choices.choices | join(sep=", ") }}]{%- endif %} diff --git a/lib/src/docs/models.rs b/lib/src/docs/models.rs index b9d08a77..a77aa6ba 100644 --- a/lib/src/docs/models.rs +++ b/lib/src/docs/models.rs @@ -60,6 +60,7 @@ pub struct SpecCommand { pub struct SpecFlag { pub name: String, pub usage: String, + pub display_usage: String, pub help: Option, pub help_long: Option, pub help_md: Option, @@ -178,13 +179,11 @@ impl From<&crate::SpecCommand> for SpecCommand { .collect(); // Calculate layout for flags - let flags_usage_col_width = max_usage_width(cmd.flags.iter().map(|f| f.usage.as_str())); - let flags: Vec = cmd - .flags - .iter() - .map(|flag| { - let mut spec_flag = SpecFlag::from(flag); - + let flags: Vec = cmd.flags.iter().map(SpecFlag::from).collect(); + let flags_usage_col_width = max_usage_width(flags.iter().map(|f| f.display_usage.as_str())); + let flags: Vec = flags + .into_iter() + .map(|mut spec_flag| { // Get help text (prefer help_long over help) let help_text = spec_flag.help_long.as_deref().or(spec_flag.help.as_deref()); @@ -241,6 +240,10 @@ impl From<&crate::SpecFlag> for SpecFlag { Self { name: flag.name.clone(), usage: flag.usage.clone(), + display_usage: flag.negate.as_ref().map_or_else( + || flag.usage.trim().to_string(), + |negate| format!("{} / {}", flag.usage.trim(), negate.trim()), + ), help: flag.help.clone(), help_long: flag.help_long.clone(), help_md: flag.help_md.clone(),