From 7a5dad89ff9753ac53171318ecc15b3a65e475bf Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 19 Sep 2022 11:54:06 -0500 Subject: [PATCH] feat(help): Break out help feature flag This removes auto-generated help, saving about 50 KiB. --- Cargo.toml | 3 +++ clap_bench/Cargo.toml | 2 +- clap_complete/Cargo.toml | 2 +- clap_complete_fig/Cargo.toml | 1 + clap_mangen/Cargo.toml | 2 +- src/_features.rs | 1 + src/builder/action.rs | 6 ++++-- src/builder/arg.rs | 34 +++++++++++++++++++++++----------- src/builder/command.rs | 15 ++++++++++++++- src/builder/debug_asserts.rs | 1 + src/builder/possible_value.rs | 8 ++++---- src/builder/styled_str.rs | 15 ++++++++------- src/error/kind.rs | 3 ++- src/output/help.rs | 31 ++++++++++++++++++++++--------- src/output/mod.rs | 7 +++++++ tests/builder/help.rs | 2 ++ tests/builder/main.rs | 1 + tests/derive/main.rs | 1 + tests/examples.rs | 1 + tests/macros.rs | 1 + tests/ui.rs | 1 + 21 files changed, 100 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 81b9f161..fc59a7e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ pre-release-replacements = [ default = [ "std", "color", + "help", "error-context", "suggestions", ] @@ -66,6 +67,7 @@ unstable-doc = ["derive", "cargo", "wrap_help", "env", "unicode", "string", "uns # Used in default std = [] # support for no_std in a backwards-compatible way color = ["dep:atty", "dep:termcolor"] +help = [] error-context = [] suggestions = ["dep:strsim", "error-context"] @@ -153,6 +155,7 @@ path = "examples/multicall-hostname.rs" [[example]] name = "repl" path = "examples/repl.rs" +required-features = ["help"] [[example]] name = "01_quick" diff --git a/clap_bench/Cargo.toml b/clap_bench/Cargo.toml index 49f30782..ce158b2f 100644 --- a/clap_bench/Cargo.toml +++ b/clap_bench/Cargo.toml @@ -11,7 +11,7 @@ publish = false release = false [dev-dependencies] -clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std"] } +clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std", "help"] } criterion = "0.3.2" lazy_static = "1" diff --git a/clap_complete/Cargo.toml b/clap_complete/Cargo.toml index 50fe07a8..838bdbd8 100644 --- a/clap_complete/Cargo.toml +++ b/clap_complete/Cargo.toml @@ -52,7 +52,7 @@ unicode-xid = { version = "0.2.2", optional = true } snapbox = { version = "0.3", features = ["diff"] } # Cutting out `filesystem` feature trycmd = { version = "0.13", default-features = false, features = ["color-auto", "diff", "examples"] } -clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std", "derive"] } +clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std", "derive", "help"] } [[example]] name = "dynamic" diff --git a/clap_complete_fig/Cargo.toml b/clap_complete_fig/Cargo.toml index dffb1227..c32ade4c 100644 --- a/clap_complete_fig/Cargo.toml +++ b/clap_complete_fig/Cargo.toml @@ -44,3 +44,4 @@ clap_complete = { path = "../clap_complete", version = "4.0.0-alpha.0" } [dev-dependencies] snapbox = { version = "0.3", features = ["diff"] } +clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std", "help"] } diff --git a/clap_mangen/Cargo.toml b/clap_mangen/Cargo.toml index bd73c601..bed0cafd 100644 --- a/clap_mangen/Cargo.toml +++ b/clap_mangen/Cargo.toml @@ -45,7 +45,7 @@ clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, feat [dev-dependencies] snapbox = { version = "0.3", features = ["diff"] } -clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std"] } +clap = { path = "../", version = "4.0.0-alpha.0", default-features = false, features = ["std", "help"] } [features] default = [] diff --git a/src/_features.rs b/src/_features.rs index 3cb1a343..e6bda356 100644 --- a/src/_features.rs +++ b/src/_features.rs @@ -6,6 +6,7 @@ //! //! * **std**: _Not Currently Used._ Placeholder for supporting `no_std` environments in a backwards compatible manner. //! * **color**: Turns on colored error messages. +//! * **help**: Auto-generate help output //! * **error-context**: Include contextual information for errors (which arg failed, etc) //! * **suggestions**: Turns on the `Did you mean '--myoption'?` feature for when users make typos. //! diff --git a/src/builder/action.rs b/src/builder/action.rs index 5a4a3c49..7cd3e017 100644 --- a/src/builder/action.rs +++ b/src/builder/action.rs @@ -2,7 +2,8 @@ /// /// # Examples /// -/// ```rust +#[cfg_attr(not(feature = "help"), doc = " ```ignore")] +#[cfg_attr(feature = "help", doc = " ```")] /// # use clap::Command; /// # use clap::Arg; /// let cmd = Command::new("mycmd") @@ -211,7 +212,8 @@ pub enum ArgAction { /// /// # Examples /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::Command; /// # use clap::Arg; /// let cmd = Command::new("mycmd") diff --git a/src/builder/arg.rs b/src/builder/arg.rs index 342bf960..dce3794b 100644 --- a/src/builder/arg.rs +++ b/src/builder/arg.rs @@ -1107,7 +1107,8 @@ impl Arg { /// # ; /// ``` /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("config") @@ -1168,7 +1169,8 @@ impl Arg { /// .value_names(["fast", "slow"]); /// ``` /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("io") @@ -1991,7 +1993,8 @@ impl Arg { /// Setting `help` displays a short message to the side of the argument when the user passes /// `-h` or `--help` (by default). /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2040,7 +2043,8 @@ impl Arg { /// Setting `help` displays a short message to the side of the argument when the user passes /// `-h` or `--help` (by default). /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2096,7 +2100,8 @@ impl Arg { /// /// # Examples /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg, ArgAction}; /// let m = Command::new("prog") /// .arg(Arg::new("a") // Typically args are grouped alphabetically by name. @@ -2162,7 +2167,8 @@ impl Arg { /// /// # Examples /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg, ArgAction}; /// let m = Command::new("prog") /// .arg(Arg::new("opt") @@ -2212,7 +2218,8 @@ impl Arg { /// /// Setting `Hidden` will hide the argument when displaying help text /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2385,7 +2392,8 @@ impl Arg { /// /// Setting `hide_short_help(true)` will hide the argument when displaying short help text /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2411,7 +2419,8 @@ impl Arg { /// /// However, when --help is called /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2456,7 +2465,8 @@ impl Arg { /// /// Setting `hide_long_help(true)` will hide the argument when displaying long help text /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -2482,7 +2492,8 @@ impl Arg { /// /// However, when -h is called /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg}; /// let m = Command::new("prog") /// .arg(Arg::new("cfg") @@ -4196,6 +4207,7 @@ impl Arg { self.is_multiple_values_set() || matches!(*self.get_action(), ArgAction::Append) } + #[cfg(feature = "help")] pub(crate) fn get_display_order(&self) -> usize { self.disp_ord.unwrap_or(999) } diff --git a/src/builder/command.rs b/src/builder/command.rs index beacc985..9f325e8a 100644 --- a/src/builder/command.rs +++ b/src/builder/command.rs @@ -90,6 +90,7 @@ pub struct Command { disp_ord: Option, term_w: Option, max_w: Option, + #[cfg(feature = "help")] template: Option, settings: AppFlags, g_settings: AppFlags, @@ -1727,6 +1728,7 @@ impl Command { /// [`Command::before_help`]: Command::before_help() /// [`Command::before_long_help`]: Command::before_long_help() #[must_use] + #[cfg(feature = "help")] pub fn help_template(mut self, s: impl IntoResettable) -> Self { self.template = s.into_resettable().into_option(); self @@ -2536,7 +2538,8 @@ impl Command { /// /// # Examples /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, }; /// let m = Command::new("cust-ord") /// .subcommand(Command::new("alpha") // typically subcommands are grouped @@ -3661,14 +3664,17 @@ impl Command { self.help_str.as_ref() } + #[cfg(feature = "help")] pub(crate) fn get_help_template(&self) -> Option<&StyledStr> { self.template.as_ref() } + #[cfg(feature = "help")] pub(crate) fn get_term_width(&self) -> Option { self.term_w } + #[cfg(feature = "help")] pub(crate) fn get_max_term_width(&self) -> Option { self.max_w } @@ -3753,6 +3759,11 @@ impl Command { self.settings.insert(AppSettings::DisableHelpFlag.into()); self.settings.insert(AppSettings::DisableVersionFlag.into()); } + if !cfg!(feature = "help") && self.get_override_help().is_none() { + self.settings.insert(AppSettings::DisableHelpFlag.into()); + self.settings + .insert(AppSettings::DisableHelpSubcommand.into()); + } if self.is_set(AppSettings::ArgsNegateSubcommands) { self.settings .insert(AppSettings::SubcommandsNegateReqs.into()); @@ -4461,6 +4472,7 @@ impl Command { .map(|sc| sc.get_name()) } + #[cfg(feature = "help")] pub(crate) fn get_display_order(&self) -> usize { self.disp_ord.unwrap_or(999) } @@ -4553,6 +4565,7 @@ impl Default for Command { disp_ord: Default::default(), term_w: Default::default(), max_w: Default::default(), + #[cfg(feature = "help")] template: Default::default(), settings: Default::default(), g_settings: Default::default(), diff --git a/src/builder/debug_asserts.rs b/src/builder/debug_asserts.rs index 92ac824e..e40d9369 100644 --- a/src/builder/debug_asserts.rs +++ b/src/builder/debug_asserts.rs @@ -337,6 +337,7 @@ pub(crate) fn assert_app(cmd: &Command) { _verify_positionals(cmd); + #[cfg(feature = "help")] if let Some(help_template) = cmd.get_help_template() { assert!( !help_template.to_string().contains("{flags}"), diff --git a/src/builder/possible_value.rs b/src/builder/possible_value.rs index 5fbca9f9..03964fe1 100644 --- a/src/builder/possible_value.rs +++ b/src/builder/possible_value.rs @@ -1,5 +1,3 @@ -use std::{borrow::Cow, iter}; - use crate::builder::IntoResettable; use crate::builder::Str; use crate::builder::StyledStr; @@ -157,6 +155,7 @@ impl PossibleValue { /// Get the help specified for this argument, if any and the argument /// value is not hidden #[inline] + #[cfg(feature = "help")] pub(crate) fn get_visible_help(&self) -> Option<&StyledStr> { if !self.hide { self.get_help() @@ -178,7 +177,8 @@ impl PossibleValue { /// Get the name if argument value is not hidden, `None` otherwise, /// but wrapped in quotes if it contains whitespace - pub(crate) fn get_visible_quoted_name(&self) -> Option> { + #[cfg(feature = "help")] + pub(crate) fn get_visible_quoted_name(&self) -> Option> { if !self.hide { Some(if self.name.contains(char::is_whitespace) { format!("{:?}", self.name).into() @@ -194,7 +194,7 @@ impl PossibleValue { /// /// Namely the name and all aliases. pub fn get_name_and_aliases(&self) -> impl Iterator + '_ { - iter::once(self.get_name()).chain(self.aliases.iter().map(|s| s.as_str())) + std::iter::once(self.get_name()).chain(self.aliases.iter().map(|s| s.as_str())) } /// Tests if the value is valid for this argument value diff --git a/src/builder/styled_str.rs b/src/builder/styled_str.rs index df92b8c4..5f2969e0 100644 --- a/src/builder/styled_str.rs +++ b/src/builder/styled_str.rs @@ -1,6 +1,3 @@ -use crate::output::display_width; -use crate::output::textwrap; - /// Terminal-styling container #[derive(Clone, Default, Debug, PartialEq, Eq)] pub struct StyledStr { @@ -86,6 +83,7 @@ impl StyledStr { self.pieces = self.pieces.trim_end().to_owned(); } + #[cfg(feature = "help")] pub(crate) fn indent(&mut self, initial: &str, trailing: &str) { if let Some((_, first)) = self.iter_mut().next() { first.insert_str(0, initial); @@ -97,8 +95,9 @@ impl StyledStr { } } + #[cfg(feature = "help")] pub(crate) fn wrap(&mut self, hard_width: usize) { - let mut wrapper = textwrap::wrap_algorithms::LineWrapper::new(hard_width); + let mut wrapper = crate::output::textwrap::wrap_algorithms::LineWrapper::new(hard_width); for (_, content) in self.iter_mut() { let mut total = Vec::new(); for (i, line) in content.split_inclusive('\n').enumerate() { @@ -106,8 +105,8 @@ impl StyledStr { // start of a section does not imply newline wrapper.reset(); } - let line = - textwrap::word_separators::find_words_ascii_space(line).collect::>(); + let line = crate::output::textwrap::word_separators::find_words_ascii_space(line) + .collect::>(); total.extend(wrapper.wrap(line)); } let total = total.join(""); @@ -130,14 +129,16 @@ impl StyledStr { } #[inline(never)] + #[cfg(feature = "help")] pub(crate) fn display_width(&self) -> usize { let mut width = 0; for (_, c) in self.iter() { - width += display_width(c); + width += crate::output::display_width(c); } width } + #[cfg(feature = "help")] pub(crate) fn is_empty(&self) -> bool { self.pieces.is_empty() } diff --git a/src/error/kind.rs b/src/error/kind.rs index 77bbad44..aa316ef3 100644 --- a/src/error/kind.rs +++ b/src/error/kind.rs @@ -250,7 +250,8 @@ pub enum ErrorKind { /// /// # Examples /// - /// ```rust + #[cfg_attr(not(feature = "help"), doc = " ```ignore")] + #[cfg_attr(feature = "help", doc = " ```")] /// # use clap::{Command, Arg, error::ErrorKind}; /// let result = Command::new("prog") /// .try_get_matches_from(vec!["prog", "--help"]); diff --git a/src/output/help.rs b/src/output/help.rs index ff1b3d24..4405d3e6 100644 --- a/src/output/help.rs +++ b/src/output/help.rs @@ -1,6 +1,6 @@ +#![cfg_attr(not(feature = "help"), allow(unused_variables))] + // Internal -use super::AutoHelp; -use super::HelpTemplate; use crate::builder::Command; use crate::builder::StyledStr; use crate::output::Usage; @@ -11,16 +11,29 @@ pub(crate) fn write_help(writer: &mut StyledStr, cmd: &Command, usage: &Usage<'_ if let Some(h) = cmd.get_override_help() { writer.extend(h.iter()); - } else if let Some(tmpl) = cmd.get_help_template() { - for (style, content) in tmpl.iter() { - if style == None { - HelpTemplate::new(writer, cmd, usage, use_long).write_templated_help(content); + } else { + #[cfg(feature = "help")] + { + use super::AutoHelp; + use super::HelpTemplate; + if let Some(tmpl) = cmd.get_help_template() { + for (style, content) in tmpl.iter() { + if style == None { + HelpTemplate::new(writer, cmd, usage, use_long) + .write_templated_help(content); + } else { + writer.stylize(style, content); + } + } } else { - writer.stylize(style, content); + AutoHelp::new(writer, cmd, usage, use_long).write_help(); } } - } else { - AutoHelp::new(writer, cmd, usage, use_long).write_help(); + + #[cfg(not(feature = "help"))] + { + debug!("write_help: no help, `Command::override_help` and `help` is missing"); + } } // Remove any extra lines caused by book keeping diff --git a/src/output/mod.rs b/src/output/mod.rs index 28985fd8..b3587116 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -1,16 +1,23 @@ mod help; +#[cfg(feature = "help")] mod help_template; mod usage; pub(crate) mod fmt; +#[cfg(feature = "help")] pub(crate) mod textwrap; pub(crate) use self::help::write_help; +#[cfg(feature = "help")] pub(crate) use self::help_template::AutoHelp; +#[cfg(feature = "help")] pub(crate) use self::help_template::HelpTemplate; +#[cfg(feature = "help")] pub(crate) use self::textwrap::core::display_width; +#[cfg(feature = "help")] pub(crate) use self::textwrap::wrap; pub(crate) use self::usage::Usage; pub(crate) const TAB: &str = " "; +#[cfg(feature = "help")] pub(crate) const TAB_WIDTH: usize = TAB.len(); diff --git a/tests/builder/help.rs b/tests/builder/help.rs index 39e4e60c..05e29208 100644 --- a/tests/builder/help.rs +++ b/tests/builder/help.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "help")] + use clap::{arg, builder::PossibleValue, error::ErrorKind, Arg, ArgAction, ArgGroup, Command}; use super::utils; diff --git a/tests/builder/main.rs b/tests/builder/main.rs index b302980d..80444d7c 100644 --- a/tests/builder/main.rs +++ b/tests/builder/main.rs @@ -1,5 +1,6 @@ #![allow(clippy::bool_assert_comparison)] #![allow(clippy::redundant_clone)] +#![cfg(feature = "help")] mod action; mod app_settings; diff --git a/tests/derive/main.rs b/tests/derive/main.rs index 33b59cdf..8de9be21 100644 --- a/tests/derive/main.rs +++ b/tests/derive/main.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "help")] #![cfg(feature = "derive")] mod app_name; diff --git a/tests/examples.rs b/tests/examples.rs index 219534f1..13d3e509 100644 --- a/tests/examples.rs +++ b/tests/examples.rs @@ -1,6 +1,7 @@ #![cfg(not(tarpaulin))] #[test] +#[cfg(feature = "help")] #[cfg(feature = "error-context")] fn example_tests() { let t = trycmd::TestCases::new(); diff --git a/tests/macros.rs b/tests/macros.rs index 7d56570f..bfe42f32 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -277,6 +277,7 @@ mod arg { } #[test] + #[cfg(feature = "help")] fn optional_value() { let mut cmd = clap::Command::new("test").arg(clap::arg!(port: -p [NUM])); diff --git a/tests/ui.rs b/tests/ui.rs index d1c6f1ff..39e5ad75 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -1,6 +1,7 @@ #![cfg(not(tarpaulin))] #[test] +#[cfg(feature = "help")] #[cfg(feature = "error-context")] fn ui_tests() { let t = trycmd::TestCases::new();