refactor(help): Decouple from parser

This commit is contained in:
Ed Page 2022-02-01 13:23:58 -06:00
parent 3cd9174f48
commit 232c17ebc4
3 changed files with 53 additions and 57 deletions

View file

@ -678,9 +678,10 @@ impl<'help> App<'help> {
self._build(); self._build();
let color = self.get_color(); let color = self.get_color();
let p = Parser::new(self);
let mut c = Colorizer::new(false, color); let mut c = Colorizer::new(false, color);
Help::new(HelpWriter::Buffer(&mut c), &p, false).write_help()?; let parser = Parser::new(self);
let usage = Usage::new(&parser.app, &parser.required);
Help::new(HelpWriter::Buffer(&mut c), &parser.app, &usage, false).write_help()?;
c.print() c.print()
} }
@ -703,9 +704,10 @@ impl<'help> App<'help> {
self._build(); self._build();
let color = self.get_color(); let color = self.get_color();
let p = Parser::new(self);
let mut c = Colorizer::new(false, color); let mut c = Colorizer::new(false, color);
Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?; let parser = Parser::new(self);
let usage = Usage::new(&parser.app, &parser.required);
Help::new(HelpWriter::Buffer(&mut c), &parser.app, &usage, true).write_help()?;
c.print() c.print()
} }
@ -728,8 +730,9 @@ impl<'help> App<'help> {
pub fn write_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> { pub fn write_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
self._build(); self._build();
let p = Parser::new(self); let parser = Parser::new(self);
Help::new(HelpWriter::Normal(w), &p, false).write_help()?; let usage = Usage::new(&parser.app, &parser.required);
Help::new(HelpWriter::Normal(w), &parser.app, &usage, false).write_help()?;
w.flush() w.flush()
} }
@ -752,8 +755,9 @@ impl<'help> App<'help> {
pub fn write_long_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> { pub fn write_long_help<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
self._build(); self._build();
let p = Parser::new(self); let parser = Parser::new(self);
Help::new(HelpWriter::Normal(w), &p, true).write_help()?; let usage = Usage::new(&parser.app, &parser.required);
Help::new(HelpWriter::Normal(w), &parser.app, &usage, true).write_help()?;
w.flush() w.flush()
} }

View file

@ -11,7 +11,6 @@ use std::{
use crate::{ use crate::{
build::{arg::display_arg_val, App, AppSettings, Arg, ArgSettings}, build::{arg::display_arg_val, App, AppSettings, Arg, ArgSettings},
output::{fmt::Colorizer, Usage}, output::{fmt::Colorizer, Usage},
parse::Parser,
}; };
// Third party // Third party
@ -21,9 +20,10 @@ use textwrap::core::display_width;
/// `clap` Help Writer. /// `clap` Help Writer.
/// ///
/// Wraps a writer stream providing different methods to generate help for `clap` objects. /// Wraps a writer stream providing different methods to generate help for `clap` objects.
pub(crate) struct Help<'help, 'app, 'parser, 'writer> { pub(crate) struct Help<'help, 'app, 'writer> {
writer: HelpWriter<'writer>, writer: HelpWriter<'writer>,
parser: &'parser Parser<'help, 'app>, app: &'app App<'help>,
usage: &'app Usage<'help, 'app>,
next_line_help: bool, next_line_help: bool,
hide_pv: bool, hide_pv: bool,
term_w: usize, term_w: usize,
@ -31,7 +31,7 @@ pub(crate) struct Help<'help, 'app, 'parser, 'writer> {
} }
// Public Functions // Public Functions
impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
const DEFAULT_TEMPLATE: &'static str = "\ const DEFAULT_TEMPLATE: &'static str = "\
{before-help}{bin} {version}\n\ {before-help}{bin} {version}\n\
{author-with-newline}{about-with-newline}\n\ {author-with-newline}{about-with-newline}\n\
@ -49,27 +49,29 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
/// Create a new `Help` instance. /// Create a new `Help` instance.
pub(crate) fn new( pub(crate) fn new(
writer: HelpWriter<'writer>, writer: HelpWriter<'writer>,
parser: &'parser Parser<'help, 'app>, app: &'app App<'help>,
usage: &'app Usage<'help, 'app>,
use_long: bool, use_long: bool,
) -> Self { ) -> Self {
debug!("Help::new"); debug!("Help::new");
let term_w = match parser.app.term_w { let term_w = match app.term_w {
Some(0) => usize::MAX, Some(0) => usize::MAX,
Some(w) => w, Some(w) => w,
None => cmp::min( None => cmp::min(
dimensions().map_or(100, |(w, _)| w), dimensions().map_or(100, |(w, _)| w),
match parser.app.max_w { match app.max_w {
None | Some(0) => usize::MAX, None | Some(0) => usize::MAX,
Some(mw) => mw, Some(mw) => mw,
}, },
), ),
}; };
let next_line_help = parser.is_set(AppSettings::NextLineHelp); let next_line_help = app.is_set(AppSettings::NextLineHelp);
let hide_pv = parser.is_set(AppSettings::HidePossibleValues); let hide_pv = app.is_set(AppSettings::HidePossibleValues);
Help { Help {
writer, writer,
parser, app,
usage,
next_line_help, next_line_help,
hide_pv, hide_pv,
term_w, term_w,
@ -81,22 +83,20 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
pub(crate) fn write_help(&mut self) -> io::Result<()> { pub(crate) fn write_help(&mut self) -> io::Result<()> {
debug!("Help::write_help"); debug!("Help::write_help");
if let Some(h) = self.parser.app.help_str { if let Some(h) = self.app.help_str {
self.none(h)?; self.none(h)?;
} else if let Some(tmpl) = self.parser.app.template { } else if let Some(tmpl) = self.app.template {
self.write_templated_help(tmpl)?; self.write_templated_help(tmpl)?;
} else { } else {
let pos = self let pos = self
.parser
.app .app
.get_positionals() .get_positionals()
.any(|arg| should_show_arg(self.use_long, arg)); .any(|arg| should_show_arg(self.use_long, arg));
let non_pos = self let non_pos = self
.parser
.app .app
.get_non_positionals() .get_non_positionals()
.any(|arg| should_show_arg(self.use_long, arg)); .any(|arg| should_show_arg(self.use_long, arg));
let subcmds = self.parser.app.has_visible_subcommands(); let subcmds = self.app.has_visible_subcommands();
if non_pos || pos || subcmds { if non_pos || pos || subcmds {
self.write_templated_help(Self::DEFAULT_TEMPLATE)?; self.write_templated_help(Self::DEFAULT_TEMPLATE)?;
@ -124,7 +124,7 @@ macro_rules! write_method {
} }
// Methods to write Arg help. // Methods to write Arg help.
impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
#[inline(never)] #[inline(never)]
fn good<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> { fn good<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
write_method!(self, msg, good) write_method!(self, msg, good)
@ -356,12 +356,9 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
fn write_before_help(&mut self) -> io::Result<()> { fn write_before_help(&mut self) -> io::Result<()> {
debug!("Help::write_before_help"); debug!("Help::write_before_help");
let before_help = if self.use_long { let before_help = if self.use_long {
self.parser self.app.before_long_help.or(self.app.before_help)
.app
.before_long_help
.or(self.parser.app.before_help)
} else { } else {
self.parser.app.before_help self.app.before_help
}; };
if let Some(output) = before_help { if let Some(output) = before_help {
self.none(text_wrapper(&output.replace("{n}", "\n"), self.term_w))?; self.none(text_wrapper(&output.replace("{n}", "\n"), self.term_w))?;
@ -373,12 +370,9 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
fn write_after_help(&mut self) -> io::Result<()> { fn write_after_help(&mut self) -> io::Result<()> {
debug!("Help::write_after_help"); debug!("Help::write_after_help");
let after_help = if self.use_long { let after_help = if self.use_long {
self.parser self.app.after_long_help.or(self.app.after_help)
.app
.after_long_help
.or(self.parser.app.after_help)
} else { } else {
self.parser.app.after_help self.app.after_help
}; };
if let Some(output) = after_help { if let Some(output) = after_help {
self.none("\n\n")?; self.none("\n\n")?;
@ -618,9 +612,9 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
fn write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> { fn write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
let about = if self.use_long { let about = if self.use_long {
self.parser.app.long_about.or(self.parser.app.about) self.app.long_about.or(self.app.about)
} else { } else {
self.parser.app.about self.app.about
}; };
if let Some(output) = about { if let Some(output) = about {
if before_new_line { if before_new_line {
@ -635,7 +629,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
} }
fn write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> { fn write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
if let Some(author) = self.parser.app.author { if let Some(author) = self.app.author {
if before_new_line { if before_new_line {
self.none("\n")?; self.none("\n")?;
} }
@ -648,7 +642,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
} }
fn write_version(&mut self) -> io::Result<()> { fn write_version(&mut self) -> io::Result<()> {
let version = self.parser.app.version.or(self.parser.app.long_version); let version = self.app.version.or(self.app.long_version);
if let Some(output) = version { if let Some(output) = version {
self.none(text_wrapper(output, self.term_w))?; self.none(text_wrapper(output, self.term_w))?;
} }
@ -657,7 +651,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
} }
/// Methods to write a single subcommand /// Methods to write a single subcommand
impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
fn write_subcommand( fn write_subcommand(
&mut self, &mut self,
sc_str: &str, sc_str: &str,
@ -731,27 +725,24 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
} }
// Methods to write Parser help. // Methods to write Parser help.
impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
/// Writes help for all arguments (options, flags, args, subcommands) /// Writes help for all arguments (options, flags, args, subcommands)
/// including titles of a Parser Object to the wrapped stream. /// including titles of a Parser Object to the wrapped stream.
pub(crate) fn write_all_args(&mut self) -> io::Result<()> { pub(crate) fn write_all_args(&mut self) -> io::Result<()> {
debug!("Help::write_all_args"); debug!("Help::write_all_args");
let pos = self let pos = self
.parser
.app .app
.get_positionals_with_no_heading() .get_positionals_with_no_heading()
.filter(|arg| should_show_arg(self.use_long, arg)) .filter(|arg| should_show_arg(self.use_long, arg))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let non_pos = self let non_pos = self
.parser
.app .app
.get_non_positionals_with_no_heading() .get_non_positionals_with_no_heading()
.filter(|arg| should_show_arg(self.use_long, arg)) .filter(|arg| should_show_arg(self.use_long, arg))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let subcmds = self.parser.app.has_visible_subcommands(); let subcmds = self.app.has_visible_subcommands();
let custom_headings = self let custom_headings = self
.parser
.app .app
.args .args
.args() .args()
@ -778,7 +769,6 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
if !custom_headings.is_empty() { if !custom_headings.is_empty() {
for heading in custom_headings { for heading in custom_headings {
let args = self let args = self
.parser
.app .app
.args .args
.args() .args()
@ -807,10 +797,10 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
self.none("\n\n")?; self.none("\n\n")?;
} }
self.warning(self.parser.app.subcommand_heading.unwrap_or("SUBCOMMANDS"))?; self.warning(self.app.subcommand_heading.unwrap_or("SUBCOMMANDS"))?;
self.warning(":\n")?; self.warning(":\n")?;
self.write_subcommands(self.parser.app)?; self.write_subcommands(self.app)?;
} }
Ok(()) Ok(())
@ -871,15 +861,15 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
fn write_bin_name(&mut self) -> io::Result<()> { fn write_bin_name(&mut self) -> io::Result<()> {
debug!("Help::write_bin_name"); debug!("Help::write_bin_name");
let bin_name = if let Some(bn) = self.parser.app.bin_name.as_ref() { let bin_name = if let Some(bn) = self.app.bin_name.as_ref() {
if bn.contains(' ') { if bn.contains(' ') {
// In case we're dealing with subcommands i.e. git mv is translated to git-mv // In case we're dealing with subcommands i.e. git mv is translated to git-mv
bn.replace(' ', "-") bn.replace(' ', "-")
} else { } else {
text_wrapper(&self.parser.app.name.replace("{n}", "\n"), self.term_w) text_wrapper(&self.app.name.replace("{n}", "\n"), self.term_w)
} }
} else { } else {
text_wrapper(&self.parser.app.name.replace("{n}", "\n"), self.term_w) text_wrapper(&self.app.name.replace("{n}", "\n"), self.term_w)
}; };
self.good(&bin_name)?; self.good(&bin_name)?;
Ok(()) Ok(())
@ -887,7 +877,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
} }
// Methods to write Parser help using templates. // Methods to write Parser help using templates.
impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { impl<'help, 'app, 'writer> Help<'help, 'app, 'writer> {
/// Write help to stream for the parser in the format defined by the template. /// Write help to stream for the parser in the format defined by the template.
/// ///
/// For details about the template language see [`App::help_template`]. /// For details about the template language see [`App::help_template`].
@ -962,7 +952,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
self.warning("USAGE:")?; self.warning("USAGE:")?;
} }
"usage" => { "usage" => {
self.none(Usage::new(&self.parser.app, &self.parser.required).create_usage_no_title(&[]))?; self.none(self.usage.create_usage_no_title(&[]))?;
} }
"all-args" => { "all-args" => {
self.write_all_args()?; self.write_all_args()?;
@ -970,13 +960,13 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
"options" => { "options" => {
// Include even those with a heading as we don't have a good way of // Include even those with a heading as we don't have a good way of
// handling help_heading in the template. // handling help_heading in the template.
self.write_args(&self.parser.app.get_non_positionals().collect::<Vec<_>>())?; self.write_args(&self.app.get_non_positionals().collect::<Vec<_>>())?;
} }
"positionals" => { "positionals" => {
self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?; self.write_args(&self.app.get_positionals().collect::<Vec<_>>())?;
} }
"subcommands" => { "subcommands" => {
self.write_subcommands(self.parser.app)?; self.write_subcommands(self.app)?;
} }
"after-help" => { "after-help" => {
self.write_after_help()?; self.write_after_help()?;

View file

@ -1569,8 +1569,9 @@ impl<'help, 'app> Parser<'help, 'app> {
} }
pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> { pub(crate) fn write_help_err(&self) -> ClapResult<Colorizer> {
let usage = Usage::new(&self.app, &self.required);
let mut c = Colorizer::new(true, self.color_help()); let mut c = Colorizer::new(true, self.color_help());
Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?; Help::new(HelpWriter::Buffer(&mut c), &self.app, &usage, false).write_help()?;
Ok(c) Ok(c)
} }
@ -1581,9 +1582,10 @@ impl<'help, 'app> Parser<'help, 'app> {
); );
use_long = use_long && self.use_long_help(); use_long = use_long && self.use_long_help();
let usage = Usage::new(&self.app, &self.required);
let mut c = Colorizer::new(false, self.color_help()); let mut c = Colorizer::new(false, self.color_help());
match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() { match Help::new(HelpWriter::Buffer(&mut c), &self.app, &usage, use_long).write_help() {
Err(e) => e.into(), Err(e) => e.into(),
_ => ClapError::for_app(self.app, c, ErrorKind::DisplayHelp, vec![]), _ => ClapError::for_app(self.app, c, ErrorKind::DisplayHelp, vec![]),
} }