4 use std
::fmt
::Write
as _
;
5 use std
::io
::{self, Write}
;
9 use crate::builder
::{display_arg_val, Arg, Command}
;
10 use crate::output
::{fmt::Colorizer, Usage}
;
11 use crate::PossibleValue
;
14 use indexmap
::IndexSet
;
15 use textwrap
::core
::display_width
;
17 /// `clap` Help Writer.
19 /// Wraps a writer stream providing different methods to generate help for `clap` objects.
20 pub(crate) struct Help
<'help
, 'cmd
, 'writer
> {
21 writer
: HelpWriter
<'writer
>,
22 cmd
: &'cmd Command
<'help
>,
23 usage
: &'cmd Usage
<'help
, 'cmd
>,
30 impl<'help
, 'cmd
, 'writer
> Help
<'help
, 'cmd
, 'writer
> {
31 #[cfg(feature = "unstable-v4")]
32 const DEFAULT_TEMPLATE
: &'
static str = "\
33 {before-help}{name} {version}\n\
34 {author-with-newline}{about-with-newline}\n\
35 {usage-heading}\n {usage}\n\
37 {all-args}{after-help}\
39 #[cfg(not(feature = "unstable-v4"))]
40 const DEFAULT_TEMPLATE
: &'
static str = "\
41 {before-help}{bin} {version}\n\
42 {author-with-newline}{about-with-newline}\n\
43 {usage-heading}\n {usage}\n\
45 {all-args}{after-help}\
48 #[cfg(feature = "unstable-v4")]
49 const DEFAULT_NO_ARGS_TEMPLATE
: &'
static str = "\
50 {before-help}{name} {version}\n\
51 {author-with-newline}{about-with-newline}\n\
52 {usage-heading}\n {usage}{after-help}\
54 #[cfg(not(feature = "unstable-v4"))]
55 const DEFAULT_NO_ARGS_TEMPLATE
: &'
static str = "\
56 {before-help}{bin} {version}\n\
57 {author-with-newline}{about-with-newline}\n\
58 {usage-heading}\n {usage}{after-help}\
61 /// Create a new `Help` instance.
63 writer
: HelpWriter
<'writer
>,
64 cmd
: &'cmd Command
<'help
>,
65 usage
: &'cmd Usage
<'help
, 'cmd
>,
68 debug
!("Help::new cmd={}, use_long={}", cmd
.get_name(), use_long
);
69 let term_w
= match cmd
.get_term_width() {
70 Some(0) => usize::MAX
,
73 dimensions().map_or(100, |(w
, _
)| w
),
74 match cmd
.get_max_term_width() {
75 None
| Some(0) => usize::MAX
,
80 let next_line_help
= cmd
.is_next_line_help_set();
92 /// Writes the parser help to the wrapped stream.
93 pub(crate) fn write_help(&mut self) -> io
::Result
<()> {
94 debug
!("Help::write_help");
96 if let Some(h
) = self.cmd
.get_override_help() {
98 } else if let Some(tmpl
) = self.cmd
.get_help_template() {
99 self.write_templated_help(tmpl
)?
;
104 .any(|arg
| should_show_arg(self.use_long
, arg
));
107 .get_non_positionals()
108 .any(|arg
| should_show_arg(self.use_long
, arg
));
109 let subcmds
= self.cmd
.has_visible_subcommands();
111 if non_pos
|| pos
|| subcmds
{
112 self.write_templated_help(Self::DEFAULT_TEMPLATE
)?
;
114 self.write_templated_help(Self::DEFAULT_NO_ARGS_TEMPLATE
)?
;
124 macro_rules
! write_method
{
125 ($_self
:ident
, $msg
:ident
, $meth
:ident
) => {
126 match &mut $_self
.writer
{
127 HelpWriter
::Buffer(c
) => {
128 c
.$
meth(($msg
).into());
131 HelpWriter
::Normal(w
) => w
.write_all($msg
.as_ref()),
136 // Methods to write Arg help.
137 impl<'help
, 'cmd
, 'writer
> Help
<'help
, 'cmd
, 'writer
> {
139 fn good
<T
: Into
<String
> + AsRef
<[u8]>>(&mut self, msg
: T
) -> io
::Result
<()> {
140 write_method
!(self, msg
, good
)
144 fn warning
<T
: Into
<String
> + AsRef
<[u8]>>(&mut self, msg
: T
) -> io
::Result
<()> {
145 write_method
!(self, msg
, warning
)
149 fn none
<T
: Into
<String
> + AsRef
<[u8]>>(&mut self, msg
: T
) -> io
::Result
<()> {
150 write_method
!(self, msg
, none
)
154 fn spaces(&mut self, n
: usize) -> io
::Result
<()> {
155 // A string with 64 consecutive spaces.
156 const SHORT_SPACE
: &str =
158 if let Some(short
) = SHORT_SPACE
.get(..n
) {
161 self.none(" ".repeat(n
))
165 /// Writes help for each argument in the order they were declared to the wrapped stream.
166 fn write_args_unsorted(&mut self, args
: &[&Arg
<'help
>]) -> io
::Result
<()> {
167 debug
!("Help::write_args_unsorted");
168 // The shortest an arg can legally be is 2 (i.e. '-x')
170 let mut arg_v
= Vec
::with_capacity(10);
174 .filter(|arg
| should_show_arg(self.use_long
, *arg
))
176 if arg
.longest_filter() {
177 longest
= longest
.max(display_width(&arg
.to_string()));
179 "Help::write_args_unsorted: arg={:?} longest={}",
187 let next_line_help
= self.will_args_wrap(args
, longest
);
189 let argc
= arg_v
.len();
190 for (i
, arg
) in arg_v
.iter().enumerate() {
191 self.write_arg(arg
, i
+ 1 == argc
, next_line_help
, longest
)?
;
196 /// Sorts arguments by length and display order and write their help to the wrapped stream.
197 fn write_args(&mut self, args
: &[&Arg
<'help
>], _category
: &str) -> io
::Result
<()> {
198 debug
!("Help::write_args {}", _category
);
199 // The shortest an arg can legally be is 2 (i.e. '-x')
201 let mut ord_v
= Vec
::new();
203 // Determine the longest
204 for &arg
in args
.iter().filter(|arg
| {
205 // If it's NextLineHelp we don't care to compute how long it is because it may be
206 // NextLineHelp on purpose simply *because* it's so long and would throw off all other
208 should_show_arg(self.use_long
, *arg
)
210 if arg
.longest_filter() {
211 longest
= longest
.max(display_width(&arg
.to_string()));
213 "Help::write_args: arg={:?} longest={}",
219 // Formatting key like this to ensure that:
220 // 1. Argument has long flags are printed just after short flags.
221 // 2. For two args both have short flags like `-c` and `-C`, the
222 // `-C` arg is printed just after the `-c` arg
223 // 3. For args without short or long flag, print them at last(sorted
225 // Example order: -a, -b, -B, -s, --select-file, --select-folder, -x
227 let key
= if let Some(x
) = arg
.short
{
228 let mut s
= x
.to_ascii_lowercase().to_string();
229 s
.push(if x
.is_ascii_lowercase() { '0' }
else { '1' }
);
231 } else if let Some(x
) = arg
.long
{
234 let mut s
= '
{'
.to_string();
235 s
.push_str(arg
.name
);
238 ord_v
.push((arg
.get_display_order(), key
, arg
));
240 ord_v
.sort_by(|a
, b
| (a
.0, &a
.1).cmp(&(b
.0, &b
.1)));
242 let next_line_help
= self.will_args_wrap(args
, longest
);
244 for (i
, (_
, _
, arg
)) in ord_v
.iter().enumerate() {
245 let last_arg
= i
+ 1 == ord_v
.len();
246 self.write_arg(arg
, last_arg
, next_line_help
, longest
)?
;
251 /// Writes help for an argument to the wrapped stream.
256 next_line_help
: bool
,
258 ) -> io
::Result
<()> {
259 let spec_vals
= &self.spec_vals(arg
);
264 self.align_to_about(arg
, next_line_help
, longest
)?
;
266 let about
= if self.use_long
{
267 arg
.long_help
.or(arg
.help
).unwrap_or("")
269 arg
.help
.or(arg
.long_help
).unwrap_or("")
272 self.help(Some(arg
), about
, spec_vals
, next_line_help
, longest
)?
;
283 /// Writes argument's short command to the wrapped stream.
284 fn short(&mut self, arg
: &Arg
<'help
>) -> io
::Result
<()> {
285 debug
!("Help::short");
289 if let Some(s
) = arg
.short
{
290 self.good(format
!("-{}", s
))
291 } else if !arg
.is_positional() {
298 /// Writes argument's long command to the wrapped stream.
299 fn long(&mut self, arg
: &Arg
<'help
>) -> io
::Result
<()> {
300 debug
!("Help::long");
301 if let Some(long
) = arg
.long
{
302 if arg
.short
.is_some() {
305 self.good(format
!("--{}", long
))?
;
310 /// Writes argument's possible values to the wrapped stream.
311 fn val(&mut self, arg
: &Arg
<'help
>) -> io
::Result
<()> {
312 debug
!("Help::val: arg={}", arg
.name
);
313 let mut need_closing_bracket
= false;
314 if arg
.is_takes_value_set() && !arg
.is_positional() {
315 let is_optional_val
= arg
.min_vals
== Some(0);
316 let sep
= if arg
.is_require_equals_set() {
318 need_closing_bracket
= true;
323 } else if is_optional_val
{
324 need_closing_bracket
= true;
332 if arg
.is_takes_value_set() || arg
.is_positional() {
335 |s
, good
| if good { self.good(s) }
else { self.none(s) }
,
339 if need_closing_bracket
{
345 /// Write alignment padding between arg's switches/values and its about message.
349 next_line_help
: bool
,
351 ) -> io
::Result
<()> {
353 "Help::align_to_about: arg={}, next_line_help={}, longest={}",
354 arg
.name
, next_line_help
, longest
356 if self.use_long
|| next_line_help
{
357 // long help prints messages on the next line so it doesn't need to align text
358 debug
!("Help::align_to_about: printing long help so skip alignment");
359 } else if !arg
.is_positional() {
360 let self_len
= display_width(&arg
.to_string());
361 // Since we're writing spaces from the tab point we first need to know if we
362 // had a long and short, or just short
363 let padding
= if arg
.long
.is_some() {
364 // Only account 4 after the val
367 // Only account for ', --' + 4 after the val
370 let spcs
= longest
+ padding
- self_len
;
372 "Help::align_to_about: positional=false arg_len={}, spaces={}",
378 let self_len
= display_width(&arg
.to_string());
380 let spcs
= longest
+ padding
- self_len
;
382 "Help::align_to_about: positional=true arg_len={}, spaces={}",
391 fn write_before_help(&mut self) -> io
::Result
<()> {
392 debug
!("Help::write_before_help");
393 let before_help
= if self.use_long
{
395 .get_before_long_help()
396 .or_else(|| self.cmd
.get_before_help())
398 self.cmd
.get_before_help()
400 if let Some(output
) = before_help
{
401 self.none(text_wrapper(&output
.replace("{n}", "\n"), self.term_w
))?
;
407 fn write_after_help(&mut self) -> io
::Result
<()> {
408 debug
!("Help::write_after_help");
409 let after_help
= if self.use_long
{
411 .get_after_long_help()
412 .or_else(|| self.cmd
.get_after_help())
414 self.cmd
.get_after_help()
416 if let Some(output
) = after_help
{
418 self.none(text_wrapper(&output
.replace("{n}", "\n"), self.term_w
))?
;
423 /// Writes argument's help to the wrapped stream.
426 arg
: Option
<&Arg
<'help
>>,
429 next_line_help
: bool
,
431 ) -> io
::Result
<()> {
432 debug
!("Help::help");
433 let mut help
= String
::from(about
) + spec_vals
;
434 debug
!("Help::help: Next Line...{:?}", next_line_help
);
436 let spaces
= if next_line_help
{
442 let too_long
= spaces
+ display_width(&help
) >= self.term_w
;
444 // Is help on next line, if so then indent
446 self.none(format
!("\n{}{}{}", TAB
, TAB
, TAB
))?
;
449 debug
!("Help::help: Too long...");
450 if too_long
&& spaces
<= self.term_w
|| help
.contains("{n}") {
452 debug
!("Help::help: help...{}", help
);
453 debug
!("Help::help: help width...{}", display_width(&help
));
454 // Determine how many newlines we need to insert
455 let avail_chars
= self.term_w
- spaces
;
456 debug
!("Help::help: Usable space...{}", avail_chars
);
457 help
= text_wrapper(&help
.replace("{n}", "\n"), avail_chars
);
461 if let Some(part
) = help
.lines().next() {
466 let spaces
= if next_line_help
{
468 } else if let Some(true) = arg
.map(|a
| a
.is_positional()) {
469 longest
+ TAB_WIDTH
* 2
471 longest
+ TAB_WIDTH
* 3
474 for part
in help
.lines().skip(1) {
476 self.spaces(spaces
)?
;
480 #[cfg(feature = "unstable-v4")]
481 if let Some(arg
) = arg
{
482 const DASH_SPACE
: usize = "- ".len();
483 const COLON_SPACE
: usize = ": ".len();
484 let possible_vals
= arg
.get_possible_values2();
486 && !arg
.is_hide_possible_values_set()
487 && possible_vals
.iter().any(PossibleValue
::should_show_help
)
489 debug
!("Help::help: Found possible vals...{:?}", possible_vals
);
490 if !help
.is_empty() {
492 self.spaces(spaces
)?
;
494 self.none("Possible values:")?
;
495 let longest
= possible_vals
497 .filter_map(|f
| f
.get_visible_quoted_name().map(|name
| display_width(&name
)))
499 .expect("Only called with possible value");
500 let help_longest
= possible_vals
502 .filter_map(|f
| f
.get_visible_help().map(display_width
))
504 .expect("Only called with possible value with help");
506 let taken
= longest
+ spaces
+ DASH_SPACE
;
508 let possible_value_new_line
=
509 self.term_w
>= taken
&& self.term_w
< taken
+ COLON_SPACE
+ help_longest
;
511 let spaces
= spaces
+ TAB_WIDTH
- DASH_SPACE
;
512 let spaces_help
= if possible_value_new_line
{
515 spaces
+ longest
+ DASH_SPACE
+ COLON_SPACE
518 for pv
in possible_vals
.iter().filter(|pv
| !pv
.is_hide_set()) {
520 self.spaces(spaces
)?
;
522 self.good(pv
.get_name())?
;
523 if let Some(help
) = pv
.get_help() {
524 debug
!("Help::help: Possible Value help");
526 if possible_value_new_line
{
528 self.spaces(spaces_help
)?
;
531 // To align help messages
532 self.spaces(longest
- display_width(pv
.get_name()))?
;
535 let avail_chars
= if self.term_w
> spaces_help
{
536 self.term_w
- spaces_help
541 let help
= text_wrapper(help
, avail_chars
);
542 let mut help
= help
.lines();
544 self.none(help
.next().unwrap_or_default())?
;
547 self.spaces(spaces_help
)?
;
557 /// Will use next line help on writing args.
558 fn will_args_wrap(&self, args
: &[&Arg
<'help
>], longest
: usize) -> bool
{
560 .filter(|arg
| should_show_arg(self.use_long
, *arg
))
562 let spec_vals
= &self.spec_vals(arg
);
563 self.arg_next_line_help(arg
, spec_vals
, longest
)
567 fn arg_next_line_help(&self, arg
: &Arg
<'help
>, spec_vals
: &str, longest
: usize) -> bool
{
568 if self.next_line_help
|| arg
.is_next_line_help_set() || self.use_long
{
573 let h
= arg
.help
.unwrap_or("");
574 let h_w
= display_width(h
) + display_width(spec_vals
);
575 let taken
= longest
+ 12;
577 && (taken
as f32 / self.term_w
as f32) > 0.40
578 && h_w
> (self.term_w
- taken
)
582 fn spec_vals(&self, a
: &Arg
) -> String
{
583 debug
!("Help::spec_vals: a={}", a
);
584 let mut spec_vals
= vec
![];
585 #[cfg(feature = "env")]
586 if let Some(ref env
) = a
.env
{
587 if !a
.is_hide_env_set() {
589 "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
592 let env_val
= if !a
.is_hide_env_values_set() {
597 .map_or(Cow
::Borrowed(""), |val
| val
.to_string_lossy())
602 let env_info
= format
!("[env: {}{}]", env
.0.to_string_lossy(), env_val
);
603 spec_vals
.push(env_info
);
606 if a
.is_takes_value_set() && !a
.is_hide_default_value_set() && !a
.default_vals
.is_empty() {
608 "Help::spec_vals: Found default value...[{:?}]",
615 .map(|&pvs
| pvs
.to_string_lossy())
617 if pvs
.contains(char::is_whitespace
) {
618 Cow
::from(format
!("{:?}", pvs
))
626 spec_vals
.push(format
!("[default: {}]", pvs
));
628 if !a
.aliases
.is_empty() {
629 debug
!("Help::spec_vals: Found aliases...{:?}", a
.aliases
);
634 .filter(|&als
| als
.1) // visible
635 .map(|&als
| als
.0) // name
640 spec_vals
.push(format
!("[aliases: {}]", als
));
644 if !a
.short_aliases
.is_empty() {
646 "Help::spec_vals: Found short aliases...{:?}",
653 .filter(|&als
| als
.1) // visible
654 .map(|&als
| als
.0.to_string()) // name
659 spec_vals
.push(format
!("[short aliases: {}]", als
));
663 let possible_vals
= a
.get_possible_values2();
664 if !(a
.is_hide_possible_values_set()
665 || possible_vals
.is_empty()
666 || cfg
!(feature
= "unstable-v4")
668 && possible_vals
.iter().any(PossibleValue
::should_show_help
))
670 debug
!("Help::spec_vals: Found possible vals...{:?}", possible_vals
);
672 let pvs
= possible_vals
674 .filter_map(PossibleValue
::get_visible_quoted_name
)
678 spec_vals
.push(format
!("[possible values: {}]", pvs
));
680 let connector
= if self.use_long { "\n" }
else { " " }
;
681 let prefix
= if !spec_vals
.is_empty() && !a
.get_help().unwrap_or("").is_empty() {
690 prefix
.to_string() + &spec_vals
.join(connector
)
693 fn write_about(&mut self, before_new_line
: bool
, after_new_line
: bool
) -> io
::Result
<()> {
694 let about
= if self.use_long
{
695 self.cmd
.get_long_about().or_else(|| self.cmd
.get_about())
699 if let Some(output
) = about
{
703 self.none(text_wrapper(output
, self.term_w
))?
;
711 fn write_author(&mut self, before_new_line
: bool
, after_new_line
: bool
) -> io
::Result
<()> {
712 if let Some(author
) = self.cmd
.get_author() {
716 self.none(text_wrapper(author
, self.term_w
))?
;
724 fn write_version(&mut self) -> io
::Result
<()> {
728 .or_else(|| self.cmd
.get_long_version());
729 if let Some(output
) = version
{
730 self.none(text_wrapper(output
, self.term_w
))?
;
736 /// Methods to write a single subcommand
737 impl<'help
, 'cmd
, 'writer
> Help
<'help
, 'cmd
, 'writer
> {
741 cmd
: &Command
<'help
>,
742 next_line_help
: bool
,
744 ) -> io
::Result
<()> {
745 debug
!("Help::write_subcommand");
747 let spec_vals
= &self.sc_spec_vals(cmd
);
751 .or_else(|| cmd
.get_long_about())
754 self.subcmd(sc_str
, next_line_help
, longest
)?
;
755 self.help(None
, about
, spec_vals
, next_line_help
, longest
)
758 fn sc_spec_vals(&self, a
: &Command
) -> String
{
759 debug
!("Help::sc_spec_vals: a={}", a
.get_name());
760 let mut spec_vals
= vec
![];
761 if 0 < a
.get_all_aliases().count() || 0 < a
.get_all_short_flag_aliases().count() {
763 "Help::spec_vals: Found aliases...{:?}",
764 a
.get_all_aliases().collect
::<Vec
<_
>>()
767 "Help::spec_vals: Found short flag aliases...{:?}",
768 a
.get_all_short_flag_aliases().collect
::<Vec
<_
>>()
771 let mut short_als
= a
772 .get_visible_short_flag_aliases()
773 .map(|a
| format
!("-{}", a
))
774 .collect
::<Vec
<_
>>();
776 let als
= a
.get_visible_aliases().map(|s
| s
.to_string());
778 short_als
.extend(als
);
780 let all_als
= short_als
.join(", ");
782 if !all_als
.is_empty() {
783 spec_vals
.push(format
!(" [aliases: {}]", all_als
));
789 fn subcommand_next_line_help(
791 cmd
: &Command
<'help
>,
795 if self.next_line_help
| self.use_long
{
800 let h
= cmd
.get_about().unwrap_or("");
801 let h_w
= display_width(h
) + display_width(spec_vals
);
802 let taken
= longest
+ 12;
804 && (taken
as f32 / self.term_w
as f32) > 0.40
805 && h_w
> (self.term_w
- taken
)
809 /// Writes subcommand to the wrapped stream.
810 fn subcmd(&mut self, sc_str
: &str, next_line_help
: bool
, longest
: usize) -> io
::Result
<()> {
814 let width
= display_width(sc_str
);
815 self.spaces(width
.max(longest
+ 4) - width
)?
;
821 // Methods to write Parser help.
822 impl<'help
, 'cmd
, 'writer
> Help
<'help
, 'cmd
, 'writer
> {
823 /// Writes help for all arguments (options, flags, args, subcommands)
824 /// including titles of a Parser Object to the wrapped stream.
825 pub(crate) fn write_all_args(&mut self) -> io
::Result
<()> {
826 debug
!("Help::write_all_args");
829 .get_positionals_with_no_heading()
830 .filter(|arg
| should_show_arg(self.use_long
, arg
))
831 .collect
::<Vec
<_
>>();
834 .get_non_positionals_with_no_heading()
835 .filter(|arg
| should_show_arg(self.use_long
, arg
))
836 .collect
::<Vec
<_
>>();
837 let subcmds
= self.cmd
.has_visible_subcommands();
839 let custom_headings
= self
842 .filter_map(|arg
| arg
.get_help_heading())
843 .collect
::<IndexSet
<_
>>();
845 let mut first
= if !pos
.is_empty() {
846 // Write positional args if any
847 self.warning("ARGS:\n")?
;
848 self.write_args_unsorted(&pos
)?
;
854 if !non_pos
.is_empty() {
858 self.warning("OPTIONS:\n")?
;
859 self.write_args(&non_pos
, "OPTIONS")?
;
862 if !custom_headings
.is_empty() {
863 for heading
in custom_headings
{
868 if let Some(help_heading
) = a
.get_help_heading() {
869 return help_heading
== heading
;
873 .filter(|arg
| should_show_arg(self.use_long
, arg
))
874 .collect
::<Vec
<_
>>();
876 if !args
.is_empty() {
880 self.warning(format
!("{}:\n", heading
))?
;
881 self.write_args(&args
, heading
)?
;
894 .get_subcommand_help_heading()
895 .unwrap_or("SUBCOMMANDS"),
897 self.warning(":\n")?
;
899 self.write_subcommands(self.cmd
)?
;
905 /// Will use next line help on writing subcommands.
906 fn will_subcommands_wrap
<'a
>(
908 subcommands
: impl IntoIterator
<Item
= &'a Command
<'help
>>,
916 .filter(|&subcommand
| should_show_subcommand(subcommand
))
918 let spec_vals
= &self.sc_spec_vals(subcommand
);
919 self.subcommand_next_line_help(subcommand
, spec_vals
, longest
)
923 /// Writes help for subcommands of a Parser Object to the wrapped stream.
924 fn write_subcommands(&mut self, cmd
: &Command
<'help
>) -> io
::Result
<()> {
925 debug
!("Help::write_subcommands");
926 // The shortest an arg can legally be is 2 (i.e. '-x')
928 let mut ord_v
= Vec
::new();
929 for subcommand
in cmd
931 .filter(|subcommand
| should_show_subcommand(subcommand
))
933 let mut sc_str
= String
::new();
934 sc_str
.push_str(subcommand
.get_name());
935 if let Some(short
) = subcommand
.get_short_flag() {
936 write
!(sc_str
, " -{}", short
).unwrap();
938 if let Some(long
) = subcommand
.get_long_flag() {
939 write
!(sc_str
, " --{}", long
).unwrap();
941 longest
= longest
.max(display_width(&sc_str
));
942 ord_v
.push((subcommand
.get_display_order(), sc_str
, subcommand
));
944 ord_v
.sort_by(|a
, b
| (a
.0, &a
.1).cmp(&(b
.0, &b
.1)));
946 debug
!("Help::write_subcommands longest = {}", longest
);
948 let next_line_help
= self.will_subcommands_wrap(cmd
.get_subcommands(), longest
);
950 let mut first
= true;
951 for (_
, sc_str
, sc
) in &ord_v
{
957 self.write_subcommand(sc_str
, sc
, next_line_help
, longest
)?
;
962 /// Writes binary name of a Parser Object to the wrapped stream.
963 fn write_display_name(&mut self) -> io
::Result
<()> {
964 debug
!("Help::write_display_name");
966 let display_name
= text_wrapper(
970 .unwrap_or_else(|| self.cmd
.get_name())
971 .replace("{n}", "\n"),
974 self.good(&display_name
)?
;
978 /// Writes binary name of a Parser Object to the wrapped stream.
979 fn write_bin_name(&mut self) -> io
::Result
<()> {
980 debug
!("Help::write_bin_name");
982 let bin_name
= if let Some(bn
) = self.cmd
.get_bin_name() {
983 if bn
.contains(' '
) {
984 // In case we're dealing with subcommands i.e. git mv is translated to git-mv
987 text_wrapper(&self.cmd
.get_name().replace("{n}", "\n"), self.term_w
)
990 text_wrapper(&self.cmd
.get_name().replace("{n}", "\n"), self.term_w
)
992 self.good(&bin_name
)?
;
997 // Methods to write Parser help using templates.
998 impl<'help
, 'cmd
, 'writer
> Help
<'help
, 'cmd
, 'writer
> {
999 /// Write help to stream for the parser in the format defined by the template.
1001 /// For details about the template language see [`Command::help_template`].
1003 /// [`Command::help_template`]: Command::help_template()
1004 fn write_templated_help(&mut self, template
: &str) -> io
::Result
<()> {
1005 debug
!("Help::write_templated_help");
1007 // The strategy is to copy the template from the reader to wrapped stream
1008 // until a tag is found. Depending on its value, the appropriate content is copied
1009 // to the wrapped stream.
1010 // The copy from template is then resumed, repeating this sequence until reading
1011 // the complete template.
1016 $
( $tag
:expr
=> $action
:stmt
)*
1021 part
if part
.starts_with(concat
!($tag
, "}")) => {
1023 let rest
= &part
[$tag
.len()+1..];
1028 // Unknown tag, write it back.
1037 let mut parts
= template
.split('
{'
);
1038 if let Some(first
) = parts
.next() {
1046 self.write_display_name()?
;
1049 self.write_bin_name()?
;
1052 self.write_version()?
;
1055 self.write_author(false, false)?
;
1057 "author-with-newline" => {
1058 self.write_author(false, true)?
;
1060 "author-section" => {
1061 self.write_author(true, true)?
;
1064 self.write_about(false, false)?
;
1066 "about-with-newline" => {
1067 self.write_about(false, true)?
;
1069 "about-section" => {
1070 self.write_about(true, true)?
;
1072 "usage-heading" => {
1073 self.warning("USAGE:")?
;
1076 self.none(self.usage
.create_usage_no_title(&[]))?
;
1079 self.write_all_args()?
;
1082 // Include even those with a heading as we don't have a good way of
1083 // handling help_heading in the template.
1084 self.write_args(&self.cmd
.get_non_positionals().collect
::<Vec
<_
>>(), "options")?
;
1087 self.write_args(&self.cmd
.get_positionals().collect
::<Vec
<_
>>(), "positionals")?
;
1090 self.write_subcommands(self.cmd
)?
;
1093 self.write_after_help()?
;
1096 self.write_before_help()?
;
1106 pub(crate) fn dimensions() -> Option
<(usize, usize)> {
1107 #[cfg(not(feature = "wrap_help"))]
1110 #[cfg(feature = "wrap_help")]
1111 terminal_size
::terminal_size().map(|(w
, h
)| (w
.0.into
(), h
.0.into
()))
1114 const TAB
: &str = " ";
1115 const TAB_WIDTH
: usize = 4;
1117 pub(crate) enum HelpWriter
<'writer
> {
1118 Normal(&'writer
mut dyn Write
),
1119 Buffer(&'writer
mut Colorizer
),
1122 fn should_show_arg(use_long
: bool
, arg
: &Arg
) -> bool
{
1123 debug
!("should_show_arg: use_long={:?}, arg={}", use_long
, arg
.name
);
1124 if arg
.is_hide_set() {
1127 (!arg
.is_hide_long_help_set() && use_long
)
1128 || (!arg
.is_hide_short_help_set() && !use_long
)
1129 || arg
.is_next_line_help_set()
1132 fn should_show_subcommand(subcommand
: &Command
) -> bool
{
1133 !subcommand
.is_hide_set()
1136 fn text_wrapper(help
: &str, width
: usize) -> String
{
1137 let wrapper
= textwrap
::Options
::new(width
)
1139 .word_splitter(textwrap
::WordSplitter
::NoHyphenation
);
1141 .map(|line
| textwrap
::fill(line
, &wrapper
))
1142 .collect
::<Vec
<String
>>()
1151 fn wrap_help_last_word() {
1152 let help
= String
::from("foo bar baz");
1153 assert_eq
!(text_wrapper(&help
, 5), "foo\nbar\nbaz");
1157 fn display_width_handles_non_ascii() {
1158 // Popular Danish tongue-twister, the name of a fruit dessert.
1159 let text
= "rødgrød med fløde";
1160 assert_eq
!(display_width(text
), 17);
1161 // Note that the string width is smaller than the string
1162 // length. This is due to the precomposed non-ASCII letters:
1163 assert_eq
!(text
.len(), 20);
1167 fn display_width_handles_emojis() {
1169 // There is a single `char`...
1170 assert_eq
!(text
.chars().count(), 1);
1171 // but it is double-width:
1172 assert_eq
!(display_width(text
), 2);
1173 // This is much less than the byte length:
1174 assert_eq
!(text
.len(), 4);