1 // Std
2 use std::{
3     borrow::Cow,
4     cmp,
5     collections::BTreeMap,
6     io::{self, Write},
7     usize,
8 };
9 
10 // Internal
11 use crate::{
12     build::{arg::display_arg_val, App, AppSettings, Arg, ArgSettings},
13     output::{fmt::Colorizer, Usage},
14     parse::Parser,
15 };
16 
17 // Third party
18 use indexmap::IndexSet;
19 use textwrap::core::display_width;
20 
21 /// `clap` Help Writer.
22 ///
23 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
24 pub(crate) struct Help<'help, 'app, 'parser, 'writer> {
25     writer: HelpWriter<'writer>,
26     parser: &'parser Parser<'help, 'app>,
27     next_line_help: bool,
28     hide_pv: bool,
29     term_w: usize,
30     use_long: bool,
31 }
32 
33 // Public Functions
34 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
35     const DEFAULT_TEMPLATE: &'static str = "\
36         {before-help}{bin} {version}\n\
37         {author-with-newline}{about-with-newline}\n\
38         {usage-heading}\n    {usage}\n\
39         \n\
40         {all-args}{after-help}\
41     ";
42 
43     const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\
44         {before-help}{bin} {version}\n\
45         {author-with-newline}{about-with-newline}\n\
46         {usage-heading}\n    {usage}{after-help}\
47     ";
48 
49     /// Create a new `Help` instance.
new( writer: HelpWriter<'writer>, parser: &'parser Parser<'help, 'app>, use_long: bool, ) -> Self50     pub(crate) fn new(
51         writer: HelpWriter<'writer>,
52         parser: &'parser Parser<'help, 'app>,
53         use_long: bool,
54     ) -> Self {
55         debug!("Help::new");
56         let term_w = match parser.app.term_w {
57             Some(0) => usize::MAX,
58             Some(w) => w,
59             None => cmp::min(
60                 dimensions().map_or(100, |(w, _)| w),
61                 match parser.app.max_w {
62                     None | Some(0) => usize::MAX,
63                     Some(mw) => mw,
64                 },
65             ),
66         };
67         let next_line_help = parser.is_set(AppSettings::NextLineHelp);
68         let hide_pv = parser.is_set(AppSettings::HidePossibleValues);
69 
70         Help {
71             writer,
72             parser,
73             next_line_help,
74             hide_pv,
75             term_w,
76             use_long,
77         }
78     }
79 
80     /// Writes the parser help to the wrapped stream.
write_help(&mut self) -> io::Result<()>81     pub(crate) fn write_help(&mut self) -> io::Result<()> {
82         debug!("Help::write_help");
83 
84         if let Some(h) = self.parser.app.help_str {
85             self.none(h)?;
86         } else if let Some(tmpl) = self.parser.app.template {
87             self.write_templated_help(tmpl)?;
88         } else {
89             let pos = self
90                 .parser
91                 .app
92                 .get_positionals()
93                 .any(|arg| should_show_arg(self.use_long, arg));
94             let non_pos = self
95                 .parser
96                 .app
97                 .get_non_positionals()
98                 .any(|arg| should_show_arg(self.use_long, arg));
99             let subcmds = self.parser.app.has_visible_subcommands();
100 
101             if non_pos || pos || subcmds {
102                 self.write_templated_help(Self::DEFAULT_TEMPLATE)?;
103             } else {
104                 self.write_templated_help(Self::DEFAULT_NO_ARGS_TEMPLATE)?;
105             }
106         }
107 
108         self.none("\n")?;
109 
110         Ok(())
111     }
112 }
113 
114 macro_rules! write_method {
115     ($_self:ident, $msg:ident, $meth:ident) => {
116         match &mut $_self.writer {
117             HelpWriter::Buffer(c) => {
118                 c.$meth(($msg).into());
119                 Ok(())
120             }
121             HelpWriter::Normal(w) => w.write_all($msg.as_ref()),
122         }
123     };
124 }
125 
126 // Methods to write Arg help.
127 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
good<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()>128     fn good<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
129         write_method!(self, msg, good)
130     }
131 
warning<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()>132     fn warning<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
133         write_method!(self, msg, warning)
134     }
135 
none<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()>136     fn none<T: Into<String> + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> {
137         write_method!(self, msg, none)
138     }
139 
spaces(&mut self, n: usize) -> io::Result<()>140     fn spaces(&mut self, n: usize) -> io::Result<()> {
141         // A string with 64 consecutive spaces.
142         const SHORT_SPACE: &str =
143             "                                                                ";
144         if let Some(short) = SHORT_SPACE.get(..n) {
145             self.none(short)
146         } else {
147             self.none(" ".repeat(n))
148         }
149     }
150 
151     /// Writes help for each argument in the order they were declared to the wrapped stream.
write_args_unsorted(&mut self, args: &[&Arg<'help>]) -> io::Result<()>152     fn write_args_unsorted(&mut self, args: &[&Arg<'help>]) -> io::Result<()> {
153         debug!("Help::write_args_unsorted");
154         // The shortest an arg can legally be is 2 (i.e. '-x')
155         let mut longest = 2;
156         let mut arg_v = Vec::with_capacity(10);
157 
158         for arg in args
159             .iter()
160             .filter(|arg| should_show_arg(self.use_long, *arg))
161         {
162             if arg.longest_filter() {
163                 longest = longest.max(display_width(arg.to_string().as_str()));
164             }
165             arg_v.push(arg)
166         }
167 
168         let next_line_help = self.will_args_wrap(args, longest);
169 
170         let argc = arg_v.len();
171         for (i, arg) in arg_v.iter().enumerate() {
172             self.write_arg(arg, i + 1 == argc, next_line_help, longest)?;
173         }
174         Ok(())
175     }
176 
177     /// Sorts arguments by length and display order and write their help to the wrapped stream.
write_args(&mut self, args: &[&Arg<'help>]) -> io::Result<()>178     fn write_args(&mut self, args: &[&Arg<'help>]) -> io::Result<()> {
179         debug!("Help::write_args");
180         // The shortest an arg can legally be is 2 (i.e. '-x')
181         let mut longest = 2;
182         let mut ord_m = BTreeMap::new();
183 
184         // Determine the longest
185         for arg in args.iter().filter(|arg| {
186             // If it's NextLineHelp we don't care to compute how long it is because it may be
187             // NextLineHelp on purpose simply *because* it's so long and would throw off all other
188             // args alignment
189             should_show_arg(self.use_long, *arg)
190         }) {
191             if arg.longest_filter() {
192                 debug!("Help::write_args: Current Longest...{}", longest);
193                 longest = longest.max(display_width(arg.to_string().as_str()));
194                 debug!("Help::write_args: New Longest...{}", longest);
195             }
196             let btm = ord_m
197                 .entry(arg.get_display_order())
198                 .or_insert_with(BTreeMap::new);
199 
200             // Formatting key like this to ensure that:
201             // 1. Argument has long flags are printed just after short flags.
202             // 2. For two args both have short flags like `-c` and `-C`, the
203             //    `-C` arg is printed just after the `-c` arg
204             // 3. For args without short or long flag, print them at last(sorted
205             //    by arg name).
206             // Example order: -a, -b, -B, -s, --select-file, --select-folder, -x
207 
208             let key = if let Some(x) = arg.short {
209                 let mut s = x.to_ascii_lowercase().to_string();
210                 s.push(if x.is_ascii_lowercase() { '0' } else { '1' });
211                 s
212             } else if let Some(x) = arg.long {
213                 x.to_string()
214             } else {
215                 let mut s = '{'.to_string();
216                 s.push_str(arg.name);
217                 s
218             };
219             btm.insert(key, arg);
220         }
221 
222         let next_line_help = self.will_args_wrap(args, longest);
223 
224         let num_ord_m = ord_m.len();
225         for (i, btm) in ord_m.values().enumerate() {
226             let last_btm = i + 1 == num_ord_m;
227             let num_args = btm.len();
228             for (i, arg) in btm.values().enumerate() {
229                 let last_arg = last_btm && i + 1 == num_args;
230                 self.write_arg(arg, last_arg, next_line_help, longest)?;
231             }
232         }
233         Ok(())
234     }
235 
236     /// Writes help for an argument to the wrapped stream.
write_arg( &mut self, arg: &Arg<'help>, last_arg: bool, next_line_help: bool, longest: usize, ) -> io::Result<()>237     fn write_arg(
238         &mut self,
239         arg: &Arg<'help>,
240         last_arg: bool,
241         next_line_help: bool,
242         longest: usize,
243     ) -> io::Result<()> {
244         let spec_vals = &self.spec_vals(arg);
245 
246         self.write_arg_inner(arg, spec_vals, next_line_help, longest)?;
247 
248         if !last_arg {
249             self.none("\n")?;
250             if next_line_help {
251                 self.none("\n")?;
252             }
253         }
254         Ok(())
255     }
256 
257     /// Writes argument's short command to the wrapped stream.
short(&mut self, arg: &Arg<'help>) -> io::Result<()>258     fn short(&mut self, arg: &Arg<'help>) -> io::Result<()> {
259         debug!("Help::short");
260 
261         self.none(TAB)?;
262 
263         if let Some(s) = arg.short {
264             self.good(&format!("-{}", s))
265         } else if !arg.is_positional() {
266             self.none(TAB)
267         } else {
268             Ok(())
269         }
270     }
271 
272     /// Writes argument's long command to the wrapped stream.
long(&mut self, arg: &Arg<'help>) -> io::Result<()>273     fn long(&mut self, arg: &Arg<'help>) -> io::Result<()> {
274         debug!("Help::long");
275         if arg.is_positional() {
276             return Ok(());
277         }
278         if arg.is_set(ArgSettings::TakesValue) {
279             if let Some(l) = arg.long {
280                 if arg.short.is_some() {
281                     self.none(", ")?;
282                 }
283                 self.good(&format!("--{}", l))?
284             }
285 
286             let sep = if arg.is_set(ArgSettings::RequireEquals) {
287                 "="
288             } else {
289                 " "
290             };
291             self.none(sep)?;
292         } else if let Some(l) = arg.long {
293             if arg.short.is_some() {
294                 self.none(", ")?;
295             }
296             self.good(&format!("--{}", l))?;
297         }
298         Ok(())
299     }
300 
301     /// Writes argument's possible values to the wrapped stream.
val(&mut self, arg: &Arg<'help>, next_line_help: bool, longest: usize) -> io::Result<()>302     fn val(&mut self, arg: &Arg<'help>, next_line_help: bool, longest: usize) -> io::Result<()> {
303         debug!("Help::val: arg={}", arg.name);
304         if arg.is_set(ArgSettings::TakesValue) || arg.is_positional() {
305             display_arg_val(
306                 arg,
307                 |s, good| if good { self.good(s) } else { self.none(s) },
308             )?;
309         }
310 
311         debug!("Help::val: Has switch...");
312         if self.use_long {
313             // long help prints messages on the next line so it don't need to align text
314             debug!("Help::val: printing long help so skip alignment");
315         } else if !arg.is_positional() {
316             debug!("Yes");
317             debug!("Help::val: nlh...{:?}", next_line_help);
318             if !next_line_help {
319                 let self_len = display_width(arg.to_string().as_str());
320                 // subtract ourself
321                 let mut spcs = longest - self_len;
322                 // Since we're writing spaces from the tab point we first need to know if we
323                 // had a long and short, or just short
324                 if arg.long.is_some() {
325                     // Only account 4 after the val
326                     spcs += 4;
327                 } else {
328                     // Only account for ', --' + 4 after the val
329                     spcs += 8;
330                 }
331 
332                 self.spaces(spcs)?;
333             }
334         } else if !next_line_help {
335             debug!("No, and not next_line");
336             self.spaces(longest + 4 - display_width(&arg.to_string()))?;
337         } else {
338             debug!("No");
339         }
340         Ok(())
341     }
342 
write_before_help(&mut self) -> io::Result<()>343     fn write_before_help(&mut self) -> io::Result<()> {
344         debug!("Help::write_before_help");
345         let before_help = if self.use_long {
346             self.parser
347                 .app
348                 .before_long_help
349                 .or(self.parser.app.before_help)
350         } else {
351             self.parser.app.before_help
352         };
353         if let Some(output) = before_help {
354             self.none(text_wrapper(output, self.term_w))?;
355             self.none("\n\n")?;
356         }
357         Ok(())
358     }
359 
write_after_help(&mut self) -> io::Result<()>360     fn write_after_help(&mut self) -> io::Result<()> {
361         debug!("Help::write_after_help");
362         let after_help = if self.use_long {
363             self.parser
364                 .app
365                 .after_long_help
366                 .or(self.parser.app.after_help)
367         } else {
368             self.parser.app.after_help
369         };
370         if let Some(output) = after_help {
371             self.none("\n\n")?;
372             self.none(text_wrapper(output, self.term_w))?;
373         }
374         Ok(())
375     }
376 
377     /// Writes argument's help to the wrapped stream.
help( &mut self, is_not_positional: bool, about: &str, spec_vals: &str, next_line_help: bool, longest: usize, ) -> io::Result<()>378     fn help(
379         &mut self,
380         is_not_positional: bool,
381         about: &str,
382         spec_vals: &str,
383         next_line_help: bool,
384         longest: usize,
385     ) -> io::Result<()> {
386         debug!("Help::help");
387         let mut help = String::from(about) + spec_vals;
388         debug!("Help::help: Next Line...{:?}", next_line_help);
389 
390         let spaces = if next_line_help {
391             12 // "tab" * 3
392         } else {
393             longest + 12
394         };
395 
396         let too_long = spaces + display_width(about) + display_width(spec_vals) >= self.term_w;
397 
398         // Is help on next line, if so then indent
399         if next_line_help {
400             self.none(&format!("\n{}{}{}", TAB, TAB, TAB))?;
401         }
402 
403         debug!("Help::help: Too long...");
404         if too_long && spaces <= self.term_w {
405             debug!("Yes");
406             debug!("Help::help: help...{}", help);
407             debug!("Help::help: help width...{}", display_width(&help));
408             // Determine how many newlines we need to insert
409             let avail_chars = self.term_w - spaces;
410             debug!("Help::help: Usable space...{}", avail_chars);
411             help = text_wrapper(&help, avail_chars);
412         } else {
413             debug!("No");
414         }
415         if let Some(part) = help.lines().next() {
416             self.none(part)?;
417         }
418         for part in help.lines().skip(1) {
419             self.none("\n")?;
420             if next_line_help {
421                 self.none(&format!("{}{}{}", TAB, TAB, TAB))?;
422             } else if is_not_positional {
423                 self.spaces(longest + 12)?;
424             } else {
425                 self.spaces(longest + 8)?;
426             }
427             self.none(part)?;
428         }
429         Ok(())
430     }
431 
432     /// Writes help for an argument to the wrapped stream.
write_arg_inner( &mut self, arg: &Arg<'help>, spec_vals: &str, next_line_help: bool, longest: usize, ) -> io::Result<()>433     fn write_arg_inner(
434         &mut self,
435         arg: &Arg<'help>,
436         spec_vals: &str,
437         next_line_help: bool,
438         longest: usize,
439     ) -> io::Result<()> {
440         self.short(arg)?;
441         self.long(arg)?;
442         self.val(arg, next_line_help, longest)?;
443 
444         let about = if self.use_long {
445             arg.long_help.unwrap_or_else(|| arg.help.unwrap_or(""))
446         } else {
447             arg.help.unwrap_or_else(|| arg.long_help.unwrap_or(""))
448         };
449 
450         self.help(
451             !arg.is_positional(),
452             about,
453             spec_vals,
454             next_line_help,
455             longest,
456         )?;
457         Ok(())
458     }
459 
460     /// Will use next line help on writing args.
will_args_wrap(&self, args: &[&Arg<'help>], longest: usize) -> bool461     fn will_args_wrap(&self, args: &[&Arg<'help>], longest: usize) -> bool {
462         args.iter()
463             .filter(|arg| should_show_arg(self.use_long, *arg))
464             .any(|arg| {
465                 let spec_vals = &self.spec_vals(arg);
466                 self.arg_next_line_help(arg, spec_vals, longest)
467             })
468     }
469 
arg_next_line_help(&self, arg: &Arg<'help>, spec_vals: &str, longest: usize) -> bool470     fn arg_next_line_help(&self, arg: &Arg<'help>, spec_vals: &str, longest: usize) -> bool {
471         if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || self.use_long {
472             // setting_next_line
473             true
474         } else {
475             // force_next_line
476             let h = arg.help.unwrap_or("");
477             let h_w = display_width(h) + display_width(spec_vals);
478             let taken = longest + 12;
479             self.term_w >= taken
480                 && (taken as f32 / self.term_w as f32) > 0.40
481                 && h_w > (self.term_w - taken)
482         }
483     }
484 
spec_vals(&self, a: &Arg) -> String485     fn spec_vals(&self, a: &Arg) -> String {
486         debug!("Help::spec_vals: a={}", a);
487         let mut spec_vals = vec![];
488         #[cfg(feature = "env")]
489         if let Some(ref env) = a.env {
490             if !a.is_set(ArgSettings::HideEnv) {
491                 debug!(
492                     "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
493                     env.0, env.1
494                 );
495                 let env_val = if !a.is_set(ArgSettings::HideEnvValues) {
496                     format!(
497                         "={}",
498                         env.1
499                             .as_ref()
500                             .map_or(Cow::Borrowed(""), |val| val.to_string_lossy())
501                     )
502                 } else {
503                     String::new()
504                 };
505                 let env_info = format!("[env: {}{}]", env.0.to_string_lossy(), env_val);
506                 spec_vals.push(env_info);
507             }
508         }
509         if !a.is_set(ArgSettings::HideDefaultValue) && !a.default_vals.is_empty() {
510             debug!(
511                 "Help::spec_vals: Found default value...[{:?}]",
512                 a.default_vals
513             );
514 
515             let pvs = a
516                 .default_vals
517                 .iter()
518                 .map(|&pvs| pvs.to_string_lossy())
519                 .map(|pvs| {
520                     if pvs.contains(char::is_whitespace) {
521                         Cow::from(format!("{:?}", pvs))
522                     } else {
523                         pvs
524                     }
525                 })
526                 .collect::<Vec<_>>()
527                 .join(" ");
528 
529             spec_vals.push(format!("[default: {}]", pvs));
530         }
531         if !a.aliases.is_empty() {
532             debug!("Help::spec_vals: Found aliases...{:?}", a.aliases);
533 
534             let als = a
535                 .aliases
536                 .iter()
537                 .filter(|&als| als.1) // visible
538                 .map(|&als| als.0) // name
539                 .collect::<Vec<_>>()
540                 .join(", ");
541 
542             if !als.is_empty() {
543                 spec_vals.push(format!("[aliases: {}]", als));
544             }
545         }
546 
547         if !a.short_aliases.is_empty() {
548             debug!(
549                 "Help::spec_vals: Found short aliases...{:?}",
550                 a.short_aliases
551             );
552 
553             let als = a
554                 .short_aliases
555                 .iter()
556                 .filter(|&als| als.1) // visible
557                 .map(|&als| als.0.to_string()) // name
558                 .collect::<Vec<_>>()
559                 .join(", ");
560 
561             if !als.is_empty() {
562                 spec_vals.push(format!("[short aliases: {}]", als));
563             }
564         }
565 
566         if !self.hide_pv
567             && !a.is_set(ArgSettings::HidePossibleValues)
568             && !a.possible_vals.is_empty()
569         {
570             debug!(
571                 "Help::spec_vals: Found possible vals...{:?}",
572                 a.possible_vals
573             );
574 
575             let pvs = a
576                 .possible_vals
577                 .iter()
578                 .filter_map(|value| {
579                     if value.is_hidden() {
580                         None
581                     } else if value.get_name().contains(char::is_whitespace) {
582                         Some(format!("{:?}", value.get_name()))
583                     } else {
584                         Some(value.get_name().to_string())
585                     }
586                 })
587                 .collect::<Vec<_>>()
588                 .join(", ");
589 
590             spec_vals.push(format!("[possible values: {}]", pvs));
591         }
592         let connector = if self.use_long { "\n" } else { " " };
593         let prefix = if !spec_vals.is_empty() && !a.get_help().unwrap_or("").is_empty() {
594             if self.use_long {
595                 "\n\n"
596             } else {
597                 " "
598             }
599         } else {
600             ""
601         };
602         prefix.to_string() + &spec_vals.join(connector)
603     }
604 
write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()>605     fn write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
606         let about = if self.use_long {
607             self.parser.app.long_about.or(self.parser.app.about)
608         } else {
609             self.parser.app.about
610         };
611         if let Some(output) = about {
612             if before_new_line {
613                 self.none("\n")?;
614             }
615             self.none(text_wrapper(output, self.term_w))?;
616             if after_new_line {
617                 self.none("\n")?;
618             }
619         }
620         Ok(())
621     }
622 
write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()>623     fn write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> {
624         if let Some(author) = self.parser.app.author {
625             if before_new_line {
626                 self.none("\n")?;
627             }
628             self.none(text_wrapper(author, self.term_w))?;
629             if after_new_line {
630                 self.none("\n")?;
631             }
632         }
633         Ok(())
634     }
635 
write_version(&mut self) -> io::Result<()>636     fn write_version(&mut self) -> io::Result<()> {
637         let version = if self.use_long {
638             self.parser.app.long_version.or(self.parser.app.version)
639         } else {
640             self.parser.app.version
641         };
642         if let Some(output) = version {
643             self.none(text_wrapper(output, self.term_w))?;
644         }
645         Ok(())
646     }
647 }
648 
649 /// Methods to write a single subcommand
650 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
write_subcommand( &mut self, sc_str: &str, app: &App<'help>, next_line_help: bool, longest: usize, ) -> io::Result<()>651     fn write_subcommand(
652         &mut self,
653         sc_str: &str,
654         app: &App<'help>,
655         next_line_help: bool,
656         longest: usize,
657     ) -> io::Result<()> {
658         debug!("Help::write_subcommand");
659 
660         let spec_vals = &self.sc_spec_vals(app);
661 
662         let about = app.about.unwrap_or_else(|| app.long_about.unwrap_or(""));
663 
664         self.subcmd(sc_str, next_line_help, longest)?;
665         self.help(false, about, spec_vals, next_line_help, longest)
666     }
667 
sc_spec_vals(&self, a: &App) -> String668     fn sc_spec_vals(&self, a: &App) -> String {
669         debug!("Help::sc_spec_vals: a={}", a.name);
670         let mut spec_vals = vec![];
671         if !a.aliases.is_empty() || !a.short_flag_aliases.is_empty() {
672             debug!("Help::spec_vals: Found aliases...{:?}", a.aliases);
673             debug!(
674                 "Help::spec_vals: Found short flag aliases...{:?}",
675                 a.short_flag_aliases
676             );
677 
678             let mut short_als = a
679                 .get_visible_short_flag_aliases()
680                 .map(|a| format!("-{}", a))
681                 .collect::<Vec<_>>();
682 
683             let als = a.get_visible_aliases().map(|s| s.to_string());
684 
685             short_als.extend(als);
686 
687             let all_als = short_als.join(", ");
688 
689             if !all_als.is_empty() {
690                 spec_vals.push(format!(" [aliases: {}]", all_als));
691             }
692         }
693         spec_vals.join(" ")
694     }
695 
subcommand_next_line_help(&self, app: &App<'help>, spec_vals: &str, longest: usize) -> bool696     fn subcommand_next_line_help(&self, app: &App<'help>, spec_vals: &str, longest: usize) -> bool {
697         if self.next_line_help | self.use_long {
698             // setting_next_line
699             true
700         } else {
701             // force_next_line
702             let h = app.about.unwrap_or("");
703             let h_w = display_width(h) + display_width(spec_vals);
704             let taken = longest + 12;
705             self.term_w >= taken
706                 && (taken as f32 / self.term_w as f32) > 0.40
707                 && h_w > (self.term_w - taken)
708         }
709     }
710 
711     /// Writes subcommand to the wrapped stream.
subcmd(&mut self, sc_str: &str, next_line_help: bool, longest: usize) -> io::Result<()>712     fn subcmd(&mut self, sc_str: &str, next_line_help: bool, longest: usize) -> io::Result<()> {
713         self.none(TAB)?;
714         self.good(sc_str)?;
715         if !next_line_help {
716             let width = display_width(sc_str);
717             self.spaces(width.max(longest + 4) - width)?;
718         }
719         Ok(())
720     }
721 }
722 
723 // Methods to write Parser help.
724 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
725     /// Writes help for all arguments (options, flags, args, subcommands)
726     /// including titles of a Parser Object to the wrapped stream.
write_all_args(&mut self) -> io::Result<()>727     pub(crate) fn write_all_args(&mut self) -> io::Result<()> {
728         debug!("Help::write_all_args");
729         let pos = self
730             .parser
731             .app
732             .get_positionals_with_no_heading()
733             .filter(|arg| should_show_arg(self.use_long, arg))
734             .collect::<Vec<_>>();
735         let non_pos = self
736             .parser
737             .app
738             .get_non_positionals_with_no_heading()
739             .filter(|arg| should_show_arg(self.use_long, arg))
740             .collect::<Vec<_>>();
741         let subcmds = self.parser.app.has_visible_subcommands();
742 
743         let custom_headings = self
744             .parser
745             .app
746             .args
747             .args()
748             .filter_map(|arg| arg.get_help_heading())
749             .collect::<IndexSet<_>>();
750 
751         let mut first = if !pos.is_empty() {
752             // Write positional args if any
753             self.warning("ARGS:\n")?;
754             self.write_args_unsorted(&pos)?;
755             false
756         } else {
757             true
758         };
759 
760         if !non_pos.is_empty() {
761             if !first {
762                 self.none("\n\n")?;
763             }
764             self.warning("OPTIONS:\n")?;
765             self.write_args(&non_pos)?;
766             first = false;
767         }
768         if !custom_headings.is_empty() {
769             for heading in custom_headings {
770                 let args = self
771                     .parser
772                     .app
773                     .args
774                     .args()
775                     .filter(|a| {
776                         if let Some(help_heading) = a.get_help_heading() {
777                             return help_heading == heading;
778                         }
779                         false
780                     })
781                     .filter(|arg| should_show_arg(self.use_long, arg))
782                     .collect::<Vec<_>>();
783 
784                 if !args.is_empty() {
785                     if !first {
786                         self.none("\n\n")?;
787                     }
788                     self.warning(&*format!("{}:\n", heading))?;
789                     self.write_args(&*args)?;
790                     first = false
791                 }
792             }
793         }
794 
795         if subcmds {
796             if !first {
797                 self.none("\n\n")?;
798             }
799 
800             self.warning(self.parser.app.subcommand_heading.unwrap_or("SUBCOMMANDS"))?;
801             self.warning(":\n")?;
802 
803             self.write_subcommands(self.parser.app)?;
804         }
805 
806         Ok(())
807     }
808 
809     /// Will use next line help on writing subcommands.
will_subcommands_wrap(&self, subcommands: &[App<'help>], longest: usize) -> bool810     fn will_subcommands_wrap(&self, subcommands: &[App<'help>], longest: usize) -> bool {
811         subcommands
812             .iter()
813             .filter(|&subcommand| should_show_subcommand(subcommand))
814             .any(|subcommand| {
815                 let spec_vals = &self.sc_spec_vals(subcommand);
816                 self.subcommand_next_line_help(subcommand, spec_vals, longest)
817             })
818     }
819 
820     /// Writes help for subcommands of a Parser Object to the wrapped stream.
write_subcommands(&mut self, app: &App<'help>) -> io::Result<()>821     fn write_subcommands(&mut self, app: &App<'help>) -> io::Result<()> {
822         debug!("Help::write_subcommands");
823         // The shortest an arg can legally be is 2 (i.e. '-x')
824         let mut longest = 2;
825         let mut ord_m = BTreeMap::new();
826         for subcommand in app
827             .subcommands
828             .iter()
829             .filter(|subcommand| should_show_subcommand(subcommand))
830         {
831             let btm = ord_m
832                 .entry(subcommand.get_display_order())
833                 .or_insert_with(BTreeMap::new);
834             let mut sc_str = String::new();
835             sc_str.push_str(
836                 &subcommand
837                     .short_flag
838                     .map_or(String::new(), |c| format!("-{}, ", c)),
839             );
840             sc_str.push_str(
841                 &subcommand
842                     .long_flag
843                     .map_or(String::new(), |c| format!("--{}, ", c)),
844             );
845             sc_str.push_str(&subcommand.name);
846             longest = longest.max(display_width(&sc_str));
847             btm.insert(sc_str, subcommand.clone());
848         }
849 
850         debug!("Help::write_subcommands longest = {}", longest);
851 
852         let next_line_help = self.will_subcommands_wrap(&app.subcommands, longest);
853 
854         let mut first = true;
855         for btm in ord_m.values() {
856             for (sc_str, sc) in btm {
857                 if first {
858                     first = false;
859                 } else {
860                     self.none("\n")?;
861                 }
862                 self.write_subcommand(sc_str, sc, next_line_help, longest)?;
863             }
864         }
865         Ok(())
866     }
867 
868     /// Writes binary name of a Parser Object to the wrapped stream.
write_bin_name(&mut self) -> io::Result<()>869     fn write_bin_name(&mut self) -> io::Result<()> {
870         debug!("Help::write_bin_name");
871 
872         let bin_name = if let Some(bn) = self.parser.app.bin_name.as_ref() {
873             if bn.contains(' ') {
874                 // In case we're dealing with subcommands i.e. git mv is translated to git-mv
875                 bn.replace(" ", "-")
876             } else {
877                 text_wrapper(&self.parser.app.name, self.term_w)
878             }
879         } else {
880             text_wrapper(&self.parser.app.name, self.term_w)
881         };
882         self.good(&bin_name)?;
883         Ok(())
884     }
885 }
886 
887 // Methods to write Parser help using templates.
888 impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> {
889     /// Write help to stream for the parser in the format defined by the template.
890     ///
891     /// For details about the template language see [`App::help_template`].
892     ///
893     /// [`App::help_template`]: App::help_template()
write_templated_help(&mut self, template: &str) -> io::Result<()>894     fn write_templated_help(&mut self, template: &str) -> io::Result<()> {
895         debug!("Help::write_templated_help");
896 
897         // The strategy is to copy the template from the reader to wrapped stream
898         // until a tag is found. Depending on its value, the appropriate content is copied
899         // to the wrapped stream.
900         // The copy from template is then resumed, repeating this sequence until reading
901         // the complete template.
902 
903         macro_rules! tags {
904             (
905                 match $part:ident {
906                     $( $tag:expr => $action:stmt )*
907                 }
908             ) => {
909                 match $part {
910                     $(
911                         part if part.starts_with(concat!($tag, "}")) => {
912                             $action
913                             let rest = &part[$tag.len()+1..];
914                             self.none(rest)?;
915                         }
916                     )*
917 
918                     // Unknown tag, write it back.
919                     part => {
920                         self.none("{")?;
921                         self.none(part)?;
922                     }
923                 }
924             };
925         }
926 
927         let mut parts = template.split('{');
928         if let Some(first) = parts.next() {
929             self.none(first)?;
930         }
931 
932         for part in parts {
933             tags! {
934                 match part {
935                     "bin" => {
936                         self.write_bin_name()?;
937                     }
938                     "version" => {
939                         self.write_version()?;
940                     }
941                     "author" => {
942                         self.write_author(false, false)?;
943                     }
944                     "author-with-newline" => {
945                         self.write_author(false, true)?;
946                     }
947                     "author-section" => {
948                         self.write_author(true, true)?;
949                     }
950                     "about" => {
951                         self.write_about(false, false)?;
952                     }
953                     "about-with-newline" => {
954                         self.write_about(false, true)?;
955                     }
956                     "about-section" => {
957                         self.write_about(true, true)?;
958                     }
959                     "usage-heading" => {
960                         self.warning("USAGE:")?;
961                     }
962                     "usage" => {
963                         self.none(Usage::new(self.parser).create_usage_no_title(&[]))?;
964                     }
965                     "all-args" => {
966                         self.write_all_args()?;
967                     }
968                     "options" => {
969                         // Include even those with a heading as we don't have a good way of
970                         // handling help_heading in the template.
971                         self.write_args(&self.parser.app.get_non_positionals().collect::<Vec<_>>())?;
972                     }
973                     "positionals" => {
974                         self.write_args(&self.parser.app.get_positionals().collect::<Vec<_>>())?;
975                     }
976                     "subcommands" => {
977                         self.write_subcommands(self.parser.app)?;
978                     }
979                     "after-help" => {
980                         self.write_after_help()?;
981                     }
982                     "before-help" => {
983                         self.write_before_help()?;
984                     }
985                 }
986             }
987         }
988 
989         Ok(())
990     }
991 }
992 
dimensions() -> Option<(usize, usize)>993 pub(crate) fn dimensions() -> Option<(usize, usize)> {
994     #[cfg(not(feature = "wrap_help"))]
995     return None;
996 
997     #[cfg(feature = "wrap_help")]
998     terminal_size::terminal_size().map(|(w, h)| (w.0.into(), h.0.into()))
999 }
1000 
1001 const TAB: &str = "    ";
1002 
1003 pub(crate) enum HelpWriter<'writer> {
1004     Normal(&'writer mut dyn Write),
1005     Buffer(&'writer mut Colorizer),
1006 }
1007 
should_show_arg(use_long: bool, arg: &Arg) -> bool1008 fn should_show_arg(use_long: bool, arg: &Arg) -> bool {
1009     debug!("should_show_arg: use_long={:?}, arg={}", use_long, arg.name);
1010     if arg.is_set(ArgSettings::Hidden) {
1011         return false;
1012     }
1013     (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long)
1014         || (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long)
1015         || arg.is_set(ArgSettings::NextLineHelp)
1016 }
1017 
should_show_subcommand(subcommand: &App) -> bool1018 fn should_show_subcommand(subcommand: &App) -> bool {
1019     !subcommand.is_set(AppSettings::Hidden)
1020 }
1021 
text_wrapper(help: &str, width: usize) -> String1022 fn text_wrapper(help: &str, width: usize) -> String {
1023     let wrapper = textwrap::Options::new(width).break_words(false);
1024     help.lines()
1025         .map(|line| textwrap::fill(line, &wrapper))
1026         .collect::<Vec<String>>()
1027         .join("\n")
1028 }
1029 
1030 #[cfg(test)]
1031 mod test {
1032     use super::*;
1033 
1034     #[test]
wrap_help_last_word()1035     fn wrap_help_last_word() {
1036         let help = String::from("foo bar baz");
1037         assert_eq!(text_wrapper(&help, 5), "foo\nbar\nbaz");
1038     }
1039 
1040     #[test]
display_width_handles_non_ascii()1041     fn display_width_handles_non_ascii() {
1042         // Popular Danish tongue-twister, the name of a fruit dessert.
1043         let text = "rødgrød med fløde";
1044         assert_eq!(display_width(text), 17);
1045         // Note that the string width is smaller than the string
1046         // length. This is due to the precomposed non-ASCII letters:
1047         assert_eq!(text.len(), 20);
1048     }
1049 
1050     #[test]
display_width_handles_emojis()1051     fn display_width_handles_emojis() {
1052         let text = "��";
1053         // There is a single `char`...
1054         assert_eq!(text.chars().count(), 1);
1055         // but it is double-width:
1056         assert_eq!(display_width(text), 2);
1057         // This is much less than the byte length:
1058         assert_eq!(text.len(), 4);
1059     }
1060 }
1061