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