]> git.proxmox.com Git - rustc.git/blame - vendor/clap/src/app/help.rs
New upstream version 1.47.0~beta.2+dfsg1
[rustc.git] / vendor / clap / src / app / help.rs
CommitLineData
8bb4bdeb 1// Std
abe05a73 2use std::borrow::Cow;
8bb4bdeb
XL
3use std::cmp;
4use std::collections::BTreeMap;
5use std::fmt::Display;
6use std::io::{self, Cursor, Read, Write};
7use std::usize;
8
9// Internal
8bb4bdeb 10use app::parser::Parser;
8faf50e0
XL
11use app::usage;
12use app::{App, AppSettings};
8bb4bdeb
XL
13use args::{AnyArg, ArgSettings, DispOrder};
14use errors::{Error, Result as ClapResult};
abe05a73 15use fmt::{Colorizer, ColorizerOption, Format};
abe05a73
XL
16use map::VecMap;
17use INTERNAL_ERROR_MSG;
8bb4bdeb
XL
18
19// Third Party
8bb4bdeb
XL
20#[cfg(feature = "wrap_help")]
21use term_size;
041b39d2 22use textwrap;
8faf50e0 23use unicode_width::UnicodeWidthStr;
8bb4bdeb
XL
24
25#[cfg(not(feature = "wrap_help"))]
26mod term_size {
8faf50e0
XL
27 pub fn dimensions() -> Option<(usize, usize)> {
28 None
29 }
8bb4bdeb
XL
30}
31
8faf50e0
XL
32fn str_width(s: &str) -> usize {
33 UnicodeWidthStr::width(s)
34}
8bb4bdeb
XL
35
36const TAB: &'static str = " ";
37
38// These are just convenient traits to make the code easier to read.
39trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
3dfed10e 40impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T where T: AnyArg<'b, 'c> + Display {}
8bb4bdeb
XL
41
42trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
43 fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
44}
45impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
abe05a73
XL
46where
47 T: ArgWithDisplay<'b, 'c> + DispOrder,
8bb4bdeb 48{
8faf50e0
XL
49 fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
50 self
51 }
8bb4bdeb
XL
52}
53
8faf50e0
XL
54fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
55 x
56}
8bb4bdeb
XL
57
58impl<'b, 'c> DispOrder for App<'b, 'c> {
8faf50e0
XL
59 fn disp_ord(&self) -> usize {
60 999
61 }
8bb4bdeb
XL
62}
63
64macro_rules! color {
65 ($_self:ident, $s:expr, $c:ident) => {
66 if $_self.color {
67 write!($_self.writer, "{}", $_self.cizer.$c($s))
68 } else {
69 write!($_self.writer, "{}", $s)
70 }
71 };
72 ($_self:ident, $fmt_s:expr, $v:expr, $c:ident) => {
73 if $_self.color {
74 write!($_self.writer, "{}", $_self.cizer.$c(format!($fmt_s, $v)))
75 } else {
76 write!($_self.writer, $fmt_s, $v)
77 }
78 };
79}
80
81/// `clap` Help Writer.
82///
83/// Wraps a writer stream providing different methods to generate help for `clap` objects.
84pub struct Help<'a> {
85 writer: &'a mut Write,
86 next_line_help: bool,
87 hide_pv: bool,
88 term_w: usize,
89 color: bool,
90 cizer: Colorizer,
91 longest: usize,
92 force_next_line: bool,
041b39d2 93 use_long: bool,
8bb4bdeb
XL
94}
95
96// Public Functions
97impl<'a> Help<'a> {
98 /// Create a new `Help` instance.
041b39d2 99 #[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
abe05a73
XL
100 pub fn new(
101 w: &'a mut Write,
102 next_line_help: bool,
103 hide_pv: bool,
104 color: bool,
105 cizer: Colorizer,
106 term_w: Option<usize>,
107 max_w: Option<usize>,
108 use_long: bool,
109 ) -> Self {
041b39d2 110 debugln!("Help::new;");
8bb4bdeb
XL
111 Help {
112 writer: w,
113 next_line_help: next_line_help,
114 hide_pv: hide_pv,
115 term_w: match term_w {
3dfed10e
XL
116 Some(width) => {
117 if width == 0 {
118 usize::MAX
119 } else {
120 width
121 }
122 }
abe05a73
XL
123 None => cmp::min(
124 term_size::dimensions().map_or(120, |(w, _)| w),
125 match max_w {
126 None | Some(0) => usize::MAX,
127 Some(mw) => mw,
128 },
129 ),
8bb4bdeb
XL
130 },
131 color: color,
132 cizer: cizer,
133 longest: 0,
134 force_next_line: false,
041b39d2 135 use_long: use_long,
8bb4bdeb
XL
136 }
137 }
138
139 /// Reads help settings from an App
140 /// and write its help to the wrapped stream.
041b39d2
XL
141 pub fn write_app_help(w: &'a mut Write, app: &App, use_long: bool) -> ClapResult<()> {
142 debugln!("Help::write_app_help;");
143 Self::write_parser_help(w, &app.p, use_long)
8bb4bdeb
XL
144 }
145
146 /// Reads help settings from a Parser
147 /// and write its help to the wrapped stream.
041b39d2
XL
148 pub fn write_parser_help(w: &'a mut Write, parser: &Parser, use_long: bool) -> ClapResult<()> {
149 debugln!("Help::write_parser_help;");
150 Self::_write_parser_help(w, parser, false, use_long)
8bb4bdeb
XL
151 }
152
153 /// Reads help settings from a Parser
154 /// and write its help to the wrapped stream which will be stderr. This method prevents
155 /// formatting when required.
156 pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
041b39d2
XL
157 debugln!("Help::write_parser_help;");
158 Self::_write_parser_help(w, parser, true, false)
8bb4bdeb
XL
159 }
160
161 #[doc(hidden)]
abe05a73
XL
162 pub fn _write_parser_help(
163 w: &'a mut Write,
164 parser: &Parser,
165 stderr: bool,
166 use_long: bool,
167 ) -> ClapResult<()> {
041b39d2 168 debugln!("Help::write_parser_help;");
8bb4bdeb
XL
169 let nlh = parser.is_set(AppSettings::NextLineHelp);
170 let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
171 let color = parser.is_set(AppSettings::ColoredHelp);
041b39d2 172 let cizer = Colorizer::new(ColorizerOption {
8bb4bdeb
XL
173 use_stderr: stderr,
174 when: parser.color(),
041b39d2 175 });
abe05a73
XL
176 Self::new(
177 w,
178 nlh,
179 hide_v,
180 color,
181 cizer,
182 parser.meta.term_w,
183 parser.meta.max_w,
184 use_long,
3dfed10e
XL
185 )
186 .write_help(parser)
8bb4bdeb
XL
187 }
188
189 /// Writes the parser help to the wrapped stream.
190 pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> {
041b39d2 191 debugln!("Help::write_help;");
8bb4bdeb 192 if let Some(h) = parser.meta.help_str {
3b2f2976 193 write!(self.writer, "{}", h).map_err(Error::from)?;
8bb4bdeb 194 } else if let Some(tmpl) = parser.meta.template {
3b2f2976 195 self.write_templated_help(parser, tmpl)?;
8bb4bdeb 196 } else {
3b2f2976 197 self.write_default_help(parser)?;
8bb4bdeb
XL
198 }
199 Ok(())
200 }
201}
202
203// Methods to write AnyArg help.
204impl<'a> Help<'a> {
205 /// Writes help for each argument in the order they were declared to the wrapped stream.
206 fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
abe05a73
XL
207 where
208 I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
8bb4bdeb 209 {
041b39d2 210 debugln!("Help::write_args_unsorted;");
8bb4bdeb
XL
211 // The shortest an arg can legally be is 2 (i.e. '-x')
212 self.longest = 2;
213 let mut arg_v = Vec::with_capacity(10);
8faf50e0
XL
214 let use_long = self.use_long;
215 for arg in args.filter(|arg| should_show_arg(use_long, *arg)) {
8bb4bdeb 216 if arg.longest_filter() {
041b39d2 217 self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
7cac9316 218 }
041b39d2 219 arg_v.push(arg)
8bb4bdeb
XL
220 }
221 let mut first = true;
222 for arg in arg_v {
223 if first {
224 first = false;
225 } else {
3b2f2976 226 self.writer.write_all(b"\n")?;
8bb4bdeb 227 }
3b2f2976 228 self.write_arg(arg.as_base())?;
8bb4bdeb
XL
229 }
230 Ok(())
231 }
232
233 /// Sorts arguments by length and display order and write their help to the wrapped stream.
234 fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
abe05a73
XL
235 where
236 I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>,
8bb4bdeb 237 {
041b39d2 238 debugln!("Help::write_args;");
8bb4bdeb
XL
239 // The shortest an arg can legally be is 2 (i.e. '-x')
240 self.longest = 2;
241 let mut ord_m = VecMap::new();
8faf50e0 242 let use_long = self.use_long;
8bb4bdeb
XL
243 // Determine the longest
244 for arg in args.filter(|arg| {
245 // If it's NextLineHelp, but we don't care to compute how long because it may be
246 // NextLineHelp on purpose *because* it's so long and would throw off all other
247 // args alignment
8faf50e0 248 should_show_arg(use_long, *arg)
8bb4bdeb
XL
249 }) {
250 if arg.longest_filter() {
041b39d2
XL
251 debugln!("Help::write_args: Current Longest...{}", self.longest);
252 self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
253 debugln!("Help::write_args: New Longest...{}", self.longest);
8bb4bdeb
XL
254 }
255 let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new());
256 btm.insert(arg.name(), arg);
257 }
258 let mut first = true;
259 for btm in ord_m.values() {
260 for arg in btm.values() {
261 if first {
262 first = false;
263 } else {
3b2f2976 264 self.writer.write_all(b"\n")?;
8bb4bdeb 265 }
3b2f2976 266 self.write_arg(arg.as_base())?;
8bb4bdeb
XL
267 }
268 }
269 Ok(())
270 }
271
272 /// Writes help for an argument to the wrapped stream.
273 fn write_arg<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
041b39d2 274 debugln!("Help::write_arg;");
3b2f2976
XL
275 self.short(arg)?;
276 self.long(arg)?;
277 let spec_vals = self.val(arg)?;
278 self.help(arg, &*spec_vals)?;
8bb4bdeb
XL
279 Ok(())
280 }
281
282 /// Writes argument's short command to the wrapped stream.
283 fn short<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
041b39d2 284 debugln!("Help::short;");
3b2f2976 285 write!(self.writer, "{}", TAB)?;
8bb4bdeb
XL
286 if let Some(s) = arg.short() {
287 color!(self, "-{}", s, good)
288 } else if arg.has_switch() {
289 write!(self.writer, "{}", TAB)
290 } else {
291 Ok(())
292 }
293 }
294
295 /// Writes argument's long command to the wrapped stream.
296 fn long<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
041b39d2 297 debugln!("Help::long;");
8bb4bdeb
XL
298 if !arg.has_switch() {
299 return Ok(());
300 }
301 if arg.takes_value() {
302 if let Some(l) = arg.long() {
303 if arg.short().is_some() {
3b2f2976 304 write!(self.writer, ", ")?;
8bb4bdeb 305 }
3b2f2976 306 color!(self, "--{}", l, good)?
8bb4bdeb 307 }
041b39d2
XL
308
309 let sep = if arg.is_set(ArgSettings::RequireEquals) {
310 "="
311 } else {
312 " "
313 };
3b2f2976 314 write!(self.writer, "{}", sep)?;
8bb4bdeb
XL
315 } else if let Some(l) = arg.long() {
316 if arg.short().is_some() {
3b2f2976 317 write!(self.writer, ", ")?;
8bb4bdeb 318 }
3b2f2976 319 color!(self, "--{}", l, good)?;
8bb4bdeb
XL
320 }
321 Ok(())
322 }
323
324 /// Writes argument's possible values to the wrapped stream.
325 fn val<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> Result<String, io::Error> {
041b39d2 326 debugln!("Help::val: arg={}", arg);
8bb4bdeb 327 if arg.takes_value() {
abe05a73
XL
328 let delim = if arg.is_set(ArgSettings::RequireDelimiter) {
329 arg.val_delim().expect(INTERNAL_ERROR_MSG)
330 } else {
331 ' '
332 };
8bb4bdeb
XL
333 if let Some(vec) = arg.val_names() {
334 let mut it = vec.iter().peekable();
335 while let Some((_, val)) = it.next() {
3b2f2976 336 color!(self, "<{}>", val, good)?;
8bb4bdeb 337 if it.peek().is_some() {
abe05a73 338 write!(self.writer, "{}", delim)?;
8bb4bdeb
XL
339 }
340 }
341 let num = vec.len();
342 if arg.is_set(ArgSettings::Multiple) && num == 1 {
3b2f2976 343 color!(self, "...", good)?;
8bb4bdeb
XL
344 }
345 } else if let Some(num) = arg.num_vals() {
346 let mut it = (0..num).peekable();
347 while let Some(_) = it.next() {
3b2f2976 348 color!(self, "<{}>", arg.name(), good)?;
8bb4bdeb 349 if it.peek().is_some() {
abe05a73 350 write!(self.writer, "{}", delim)?;
8bb4bdeb
XL
351 }
352 }
353 if arg.is_set(ArgSettings::Multiple) && num == 1 {
3b2f2976 354 color!(self, "...", good)?;
8bb4bdeb
XL
355 }
356 } else if arg.has_switch() {
3b2f2976 357 color!(self, "<{}>", arg.name(), good)?;
8bb4bdeb 358 if arg.is_set(ArgSettings::Multiple) {
3b2f2976 359 color!(self, "...", good)?;
8bb4bdeb
XL
360 }
361 } else {
3b2f2976 362 color!(self, "{}", arg, good)?;
8bb4bdeb
XL
363 }
364 }
365
366 let spec_vals = self.spec_vals(arg);
367 let h = arg.help().unwrap_or("");
368 let h_w = str_width(h) + str_width(&*spec_vals);
369 let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
370 let taken = self.longest + 12;
3dfed10e
XL
371 self.force_next_line = !nlh
372 && self.term_w >= taken
abe05a73
XL
373 && (taken as f32 / self.term_w as f32) > 0.40
374 && h_w > (self.term_w - taken);
8bb4bdeb 375
041b39d2 376 debug!("Help::val: Has switch...");
8bb4bdeb
XL
377 if arg.has_switch() {
378 sdebugln!("Yes");
041b39d2
XL
379 debugln!("Help::val: force_next_line...{:?}", self.force_next_line);
380 debugln!("Help::val: nlh...{:?}", nlh);
381 debugln!("Help::val: taken...{}", taken);
abe05a73
XL
382 debugln!(
383 "Help::val: help_width > (width - taken)...{} > ({} - {})",
384 h_w,
385 self.term_w,
386 taken
387 );
041b39d2
XL
388 debugln!("Help::val: longest...{}", self.longest);
389 debug!("Help::val: next_line...");
8bb4bdeb
XL
390 if !(nlh || self.force_next_line) {
391 sdebugln!("No");
041b39d2 392 let self_len = str_width(arg.to_string().as_str());
8bb4bdeb
XL
393 // subtract ourself
394 let mut spcs = self.longest - self_len;
395 // Since we're writing spaces from the tab point we first need to know if we
396 // had a long and short, or just short
397 if arg.long().is_some() {
398 // Only account 4 after the val
399 spcs += 4;
400 } else {
401 // Only account for ', --' + 4 after the val
402 spcs += 8;
403 }
404
405 write_nspaces!(self.writer, spcs);
406 } else {
407 sdebugln!("Yes");
408 }
409 } else if !(nlh || self.force_next_line) {
410 sdebugln!("No, and not next_line");
abe05a73
XL
411 write_nspaces!(
412 self.writer,
413 self.longest + 4 - (str_width(arg.to_string().as_str()))
414 );
8bb4bdeb
XL
415 } else {
416 sdebugln!("No");
417 }
418 Ok(spec_vals)
419 }
420
421 fn write_before_after_help(&mut self, h: &str) -> io::Result<()> {
041b39d2
XL
422 debugln!("Help::write_before_after_help;");
423 let mut help = String::from(h);
8bb4bdeb 424 // determine if our help fits or needs to wrap
abe05a73
XL
425 debugln!(
426 "Help::write_before_after_help: Term width...{}",
427 self.term_w
428 );
8bb4bdeb
XL
429 let too_long = str_width(h) >= self.term_w;
430
041b39d2 431 debug!("Help::write_before_after_help: Too long...");
8bb4bdeb
XL
432 if too_long || h.contains("{n}") {
433 sdebugln!("Yes");
041b39d2 434 debugln!("Help::write_before_after_help: help: {}", help);
abe05a73
XL
435 debugln!(
436 "Help::write_before_after_help: help width: {}",
437 str_width(&*help)
438 );
8bb4bdeb 439 // Determine how many newlines we need to insert
abe05a73
XL
440 debugln!(
441 "Help::write_before_after_help: Usable space: {}",
442 self.term_w
443 );
041b39d2 444 help = wrap_help(&help.replace("{n}", "\n"), self.term_w);
8bb4bdeb
XL
445 } else {
446 sdebugln!("No");
447 }
3b2f2976 448 write!(self.writer, "{}", help)?;
8bb4bdeb
XL
449 Ok(())
450 }
451
452 /// Writes argument's help to the wrapped stream.
453 fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> {
041b39d2 454 debugln!("Help::help;");
8faf50e0 455 let h = if self.use_long && arg.name() != "" {
041b39d2
XL
456 arg.long_help().unwrap_or_else(|| arg.help().unwrap_or(""))
457 } else {
458 arg.help().unwrap_or_else(|| arg.long_help().unwrap_or(""))
459 };
460 let mut help = String::from(h) + spec_vals;
3dfed10e
XL
461 let nlh = self.next_line_help
462 || arg.is_set(ArgSettings::NextLineHelp)
463 || (self.use_long && arg.name() != "");
041b39d2 464 debugln!("Help::help: Next Line...{:?}", nlh);
8bb4bdeb
XL
465
466 let spcs = if nlh || self.force_next_line {
467 12 // "tab" * 3
468 } else {
469 self.longest + 12
470 };
471
472 let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= self.term_w;
473
474 // Is help on next line, if so then indent
475 if nlh || self.force_next_line {
3b2f2976 476 write!(self.writer, "\n{}{}{}", TAB, TAB, TAB)?;
8bb4bdeb
XL
477 }
478
041b39d2 479 debug!("Help::help: Too long...");
8bb4bdeb
XL
480 if too_long && spcs <= self.term_w || h.contains("{n}") {
481 sdebugln!("Yes");
041b39d2
XL
482 debugln!("Help::help: help...{}", help);
483 debugln!("Help::help: help width...{}", str_width(&*help));
8bb4bdeb
XL
484 // Determine how many newlines we need to insert
485 let avail_chars = self.term_w - spcs;
041b39d2
XL
486 debugln!("Help::help: Usable space...{}", avail_chars);
487 help = wrap_help(&help.replace("{n}", "\n"), avail_chars);
8bb4bdeb
XL
488 } else {
489 sdebugln!("No");
490 }
041b39d2 491 if let Some(part) = help.lines().next() {
3b2f2976 492 write!(self.writer, "{}", part)?;
041b39d2
XL
493 }
494 for part in help.lines().skip(1) {
3b2f2976 495 write!(self.writer, "\n")?;
041b39d2 496 if nlh || self.force_next_line {
3b2f2976 497 write!(self.writer, "{}{}{}", TAB, TAB, TAB)?;
041b39d2
XL
498 } else if arg.has_switch() {
499 write_nspaces!(self.writer, self.longest + 12);
500 } else {
501 write_nspaces!(self.writer, self.longest + 8);
8bb4bdeb 502 }
3b2f2976 503 write!(self.writer, "{}", part)?;
041b39d2
XL
504 }
505 if !help.contains('\n') && (nlh || self.force_next_line) {
3b2f2976 506 write!(self.writer, "\n")?;
8bb4bdeb
XL
507 }
508 Ok(())
509 }
510
511 fn spec_vals(&self, a: &ArgWithDisplay) -> String {
041b39d2 512 debugln!("Help::spec_vals: a={}", a);
8bb4bdeb 513 let mut spec_vals = vec![];
abe05a73
XL
514 if let Some(ref env) = a.env() {
515 debugln!(
516 "Help::spec_vals: Found environment variable...[{:?}:{:?}]",
517 env.0,
518 env.1
519 );
ff7c6d11 520 let env_val = if !a.is_set(ArgSettings::HideEnvValues) {
8faf50e0
XL
521 format!(
522 "={}",
523 env.1.map_or(Cow::Borrowed(""), |val| val.to_string_lossy())
524 )
ff7c6d11
XL
525 } else {
526 String::new()
527 };
8faf50e0 528 let env_info = format!(" [env: {}{}]", env.0.to_string_lossy(), env_val);
ff7c6d11 529 spec_vals.push(env_info);
abe05a73 530 }
041b39d2
XL
531 if !a.is_set(ArgSettings::HideDefaultValue) {
532 if let Some(pv) = a.default_val() {
533 debugln!("Help::spec_vals: Found default value...[{:?}]", pv);
abe05a73
XL
534 spec_vals.push(format!(
535 " [default: {}]",
536 if self.color {
537 self.cizer.good(pv.to_string_lossy())
538 } else {
539 Format::None(pv.to_string_lossy())
540 }
541 ));
041b39d2 542 }
8bb4bdeb
XL
543 }
544 if let Some(ref aliases) = a.aliases() {
041b39d2 545 debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
abe05a73
XL
546 spec_vals.push(format!(
547 " [aliases: {}]",
548 if self.color {
549 aliases
550 .iter()
551 .map(|v| format!("{}", self.cizer.good(v)))
552 .collect::<Vec<_>>()
553 .join(", ")
554 } else {
555 aliases.join(", ")
556 }
557 ));
8bb4bdeb
XL
558 }
559 if !self.hide_pv && !a.is_set(ArgSettings::HidePossibleValues) {
560 if let Some(pv) = a.possible_vals() {
041b39d2 561 debugln!("Help::spec_vals: Found possible vals...{:?}", pv);
8bb4bdeb 562 spec_vals.push(if self.color {
abe05a73 563 format!(
0531ce1d 564 " [possible values: {}]",
abe05a73
XL
565 pv.iter()
566 .map(|v| format!("{}", self.cizer.good(v)))
567 .collect::<Vec<_>>()
568 .join(", ")
569 )
570 } else {
0531ce1d 571 format!(" [possible values: {}]", pv.join(", "))
abe05a73 572 });
8bb4bdeb
XL
573 }
574 }
575 spec_vals.join(" ")
576 }
577}
578
8faf50e0
XL
579fn should_show_arg(use_long: bool, arg: &ArgWithOrder) -> bool {
580 if arg.is_set(ArgSettings::Hidden) {
581 return false;
582 }
583
584 (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long)
585 || (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long)
586 || arg.is_set(ArgSettings::NextLineHelp)
587}
8bb4bdeb
XL
588
589// Methods to write Parser help.
590impl<'a> Help<'a> {
591 /// Writes help for all arguments (options, flags, args, subcommands)
592 /// including titles of a Parser Object to the wrapped stream.
593 #[cfg_attr(feature = "lints", allow(useless_let_if_seq))]
041b39d2 594 #[cfg_attr(feature = "cargo-clippy", allow(useless_let_if_seq))]
8bb4bdeb 595 pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
041b39d2 596 debugln!("Help::write_all_args;");
8bb4bdeb 597 let flags = parser.has_flags();
041b39d2
XL
598 let pos = parser
599 .positionals()
600 .filter(|arg| !arg.is_set(ArgSettings::Hidden))
3dfed10e
XL
601 .count()
602 > 0;
8bb4bdeb 603 let opts = parser.has_opts();
ea8adc8c 604 let subcmds = parser.has_visible_subcommands();
8bb4bdeb
XL
605
606 let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
607
608 let mut first = true;
609
610 if unified_help && (flags || opts) {
041b39d2
XL
611 let opts_flags = parser
612 .flags()
8bb4bdeb
XL
613 .map(as_arg_trait)
614 .chain(parser.opts().map(as_arg_trait));
3b2f2976
XL
615 color!(self, "OPTIONS:\n", warning)?;
616 self.write_args(opts_flags)?;
8bb4bdeb
XL
617 first = false;
618 } else {
619 if flags {
3b2f2976
XL
620 color!(self, "FLAGS:\n", warning)?;
621 self.write_args(parser.flags().map(as_arg_trait))?;
8bb4bdeb
XL
622 first = false;
623 }
624 if opts {
625 if !first {
3b2f2976 626 self.writer.write_all(b"\n\n")?;
8bb4bdeb 627 }
3b2f2976
XL
628 color!(self, "OPTIONS:\n", warning)?;
629 self.write_args(parser.opts().map(as_arg_trait))?;
8bb4bdeb
XL
630 first = false;
631 }
632 }
633
634 if pos {
635 if !first {
3b2f2976 636 self.writer.write_all(b"\n\n")?;
8bb4bdeb 637 }
3b2f2976
XL
638 color!(self, "ARGS:\n", warning)?;
639 self.write_args_unsorted(parser.positionals().map(as_arg_trait))?;
8bb4bdeb
XL
640 first = false;
641 }
642
643 if subcmds {
644 if !first {
3b2f2976 645 self.writer.write_all(b"\n\n")?;
8bb4bdeb 646 }
3b2f2976
XL
647 color!(self, "SUBCOMMANDS:\n", warning)?;
648 self.write_subcommands(parser)?;
8bb4bdeb
XL
649 }
650
651 Ok(())
652 }
653
654 /// Writes help for subcommands of a Parser Object to the wrapped stream.
655 fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> {
041b39d2 656 debugln!("Help::write_subcommands;");
8bb4bdeb
XL
657 // The shortest an arg can legally be is 2 (i.e. '-x')
658 self.longest = 2;
659 let mut ord_m = VecMap::new();
041b39d2 660 for sc in parser
abe05a73
XL
661 .subcommands
662 .iter()
663 .filter(|s| !s.p.is_set(AppSettings::Hidden))
664 {
665 let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
041b39d2
XL
666 self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str()));
667 //self.longest = cmp::max(self.longest, sc.p.meta.name.len());
8bb4bdeb
XL
668 btm.insert(sc.p.meta.name.clone(), sc.clone());
669 }
670
671 let mut first = true;
672 for btm in ord_m.values() {
673 for sc in btm.values() {
674 if first {
675 first = false;
676 } else {
3b2f2976 677 self.writer.write_all(b"\n")?;
8bb4bdeb 678 }
3b2f2976 679 self.write_arg(sc)?;
8bb4bdeb
XL
680 }
681 }
682 Ok(())
683 }
684
685 /// Writes version of a Parser Object to the wrapped stream.
686 fn write_version(&mut self, parser: &Parser) -> io::Result<()> {
041b39d2 687 debugln!("Help::write_version;");
3b2f2976 688 write!(self.writer, "{}", parser.meta.version.unwrap_or(""))?;
8bb4bdeb
XL
689 Ok(())
690 }
691
692 /// Writes binary name of a Parser Object to the wrapped stream.
693 fn write_bin_name(&mut self, parser: &Parser) -> io::Result<()> {
041b39d2
XL
694 debugln!("Help::write_bin_name;");
695 macro_rules! write_name {
696 () => {{
697 let mut name = parser.meta.name.clone();
698 name = name.replace("{n}", "\n");
3b2f2976 699 color!(self, wrap_help(&name, self.term_w), good)?;
041b39d2
XL
700 }};
701 }
8bb4bdeb
XL
702 if let Some(bn) = parser.meta.bin_name.as_ref() {
703 if bn.contains(' ') {
704 // Incase we're dealing with subcommands i.e. git mv is translated to git-mv
3b2f2976 705 color!(self, bn.replace(" ", "-"), good)?
8bb4bdeb 706 } else {
041b39d2 707 write_name!();
8bb4bdeb
XL
708 }
709 } else {
041b39d2 710 write_name!();
8bb4bdeb
XL
711 }
712 Ok(())
713 }
714
715 /// Writes default help for a Parser Object to the wrapped stream.
716 pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> {
041b39d2 717 debugln!("Help::write_default_help;");
8bb4bdeb 718 if let Some(h) = parser.meta.pre_help {
3b2f2976
XL
719 self.write_before_after_help(h)?;
720 self.writer.write_all(b"\n\n")?;
8bb4bdeb
XL
721 }
722
041b39d2
XL
723 macro_rules! write_thing {
724 ($thing:expr) => {{
725 let mut owned_thing = $thing.to_owned();
726 owned_thing = owned_thing.replace("{n}", "\n");
8faf50e0 727 write!(self.writer, "{}\n", wrap_help(&owned_thing, self.term_w))?
041b39d2
XL
728 }};
729 }
8bb4bdeb 730 // Print the version
3b2f2976
XL
731 self.write_bin_name(parser)?;
732 self.writer.write_all(b" ")?;
733 self.write_version(parser)?;
734 self.writer.write_all(b"\n")?;
8bb4bdeb 735 if let Some(author) = parser.meta.author {
041b39d2 736 write_thing!(author)
8bb4bdeb 737 }
8faf50e0
XL
738 // if self.use_long {
739 // if let Some(about) = parser.meta.long_about {
740 // debugln!("Help::write_default_help: writing long about");
741 // write_thing!(about)
742 // } else if let Some(about) = parser.meta.about {
743 // debugln!("Help::write_default_help: writing about");
744 // write_thing!(about)
745 // }
746 // } else
747 if let Some(about) = parser.meta.long_about {
748 debugln!("Help::write_default_help: writing long about");
749 write_thing!(about)
abe05a73
XL
750 } else if let Some(about) = parser.meta.about {
751 debugln!("Help::write_default_help: writing about");
752 write_thing!(about)
8bb4bdeb
XL
753 }
754
3b2f2976 755 color!(self, "\nUSAGE:", warning)?;
abe05a73
XL
756 write!(
757 self.writer,
758 "\n{}{}\n\n",
759 TAB,
760 usage::create_usage_no_title(parser, &[])
761 )?;
8bb4bdeb
XL
762
763 let flags = parser.has_flags();
764 let pos = parser.has_positionals();
765 let opts = parser.has_opts();
766 let subcmds = parser.has_subcommands();
767
768 if flags || opts || pos || subcmds {
3b2f2976 769 self.write_all_args(parser)?;
8bb4bdeb
XL
770 }
771
772 if let Some(h) = parser.meta.more_help {
773 if flags || opts || pos || subcmds {
3b2f2976 774 self.writer.write_all(b"\n\n")?;
8bb4bdeb 775 }
3b2f2976 776 self.write_before_after_help(h)?;
8bb4bdeb
XL
777 }
778
779 self.writer.flush().map_err(Error::from)
780 }
781}
782
783/// Possible results for a copying function that stops when a given
784/// byte was found.
785enum CopyUntilResult {
786 DelimiterFound(usize),
787 DelimiterNotFound(usize),
788 ReaderEmpty,
789 ReadError(io::Error),
790 WriteError(io::Error),
791}
792
793/// Copies the contents of a reader into a writer until a delimiter byte is found.
794/// On success, the total number of bytes that were
795/// copied from reader to writer is returned.
796fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> CopyUntilResult {
041b39d2 797 debugln!("copy_until;");
8bb4bdeb
XL
798
799 let mut count = 0;
800 for wb in r.bytes() {
801 match wb {
802 Ok(b) => {
803 if b == delimiter_byte {
804 return CopyUntilResult::DelimiterFound(count);
805 }
806 match w.write(&[b]) {
807 Ok(c) => count += c,
808 Err(e) => return CopyUntilResult::WriteError(e),
809 }
810 }
811 Err(e) => return CopyUntilResult::ReadError(e),
812 }
813 }
814 if count > 0 {
815 CopyUntilResult::DelimiterNotFound(count)
816 } else {
817 CopyUntilResult::ReaderEmpty
818 }
819}
820
821/// Copies the contents of a reader into a writer until a {tag} is found,
822/// copying the tag content to a buffer and returning its size.
823/// In addition to errors, there are three possible outputs:
041b39d2
XL
824/// - `None`: The reader was consumed.
825/// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
826/// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
abe05a73
XL
827fn copy_and_capture<R: Read, W: Write>(
828 r: &mut R,
829 w: &mut W,
830 tag_buffer: &mut Cursor<Vec<u8>>,
831) -> Option<io::Result<usize>> {
8bb4bdeb 832 use self::CopyUntilResult::*;
041b39d2 833 debugln!("copy_and_capture;");
8bb4bdeb
XL
834
835 // Find the opening byte.
836 match copy_until(r, w, b'{') {
8bb4bdeb
XL
837 // The end of the reader was reached without finding the opening tag.
838 // (either with or without having copied data to the writer)
839 // Return None indicating that we are done.
abe05a73 840 ReaderEmpty | DelimiterNotFound(_) => None,
8bb4bdeb
XL
841
842 // Something went wrong.
843 ReadError(e) | WriteError(e) => Some(Err(e)),
844
845 // The opening byte was found.
846 // (either with or without having copied data to the writer)
847 DelimiterFound(_) => {
8bb4bdeb
XL
848 // Lets reset the buffer first and find out how long it is.
849 tag_buffer.set_position(0);
850 let buffer_size = tag_buffer.get_ref().len();
851
852 // Find the closing byte,limiting the reader to the length of the buffer.
853 let mut rb = r.take(buffer_size as u64);
854 match copy_until(&mut rb, tag_buffer, b'}') {
8bb4bdeb
XL
855 // We were already at the end of the reader.
856 // Return None indicating that we are done.
857 ReaderEmpty => None,
858
859 // The closing tag was found.
860 // Return the tag_length.
861 DelimiterFound(tag_length) => Some(Ok(tag_length)),
862
863 // The end of the reader was found without finding the closing tag.
864 // Write the opening byte and captured text to the writer.
0531ce1d 865 // Return 0 indicating that nothing was captured but the reader still contains data.
abe05a73
XL
866 DelimiterNotFound(not_tag_length) => match w.write(b"{") {
867 Err(e) => Some(Err(e)),
868 _ => match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
8bb4bdeb 869 Err(e) => Some(Err(e)),
abe05a73
XL
870 _ => Some(Ok(0)),
871 },
872 },
8bb4bdeb
XL
873
874 ReadError(e) | WriteError(e) => Some(Err(e)),
875 }
876 }
877 }
878}
879
8bb4bdeb
XL
880// Methods to write Parser help using templates.
881impl<'a> Help<'a> {
882 /// Write help to stream for the parser in the format defined by the template.
883 ///
884 /// Tags arg given inside curly brackets:
885 /// Valid tags are:
886 /// * `{bin}` - Binary name.
887 /// * `{version}` - Version number.
888 /// * `{author}` - Author information.
889 /// * `{usage}` - Automatically generated or given usage string.
890 /// * `{all-args}` - Help for all arguments (options, flags, positionals arguments,
891 /// and subcommands) including titles.
892 /// * `{unified}` - Unified help for options and flags.
893 /// * `{flags}` - Help for flags.
894 /// * `{options}` - Help for options.
895 /// * `{positionals}` - Help for positionals arguments.
896 /// * `{subcommands}` - Help for subcommands.
897 /// * `{after-help}` - Info to be displayed after the help message.
898 /// * `{before-help}` - Info to be displayed before the help message.
899 ///
8faf50e0 900 /// The template system is, on purpose, very simple. Therefore the tags have to written
8bb4bdeb
XL
901 /// in the lowercase and without spacing.
902 fn write_templated_help(&mut self, parser: &Parser, template: &str) -> ClapResult<()> {
041b39d2 903 debugln!("Help::write_templated_help;");
8bb4bdeb
XL
904 let mut tmplr = Cursor::new(&template);
905 let mut tag_buf = Cursor::new(vec![0u8; 15]);
906
0531ce1d 907 // The strategy is to copy the template from the reader to wrapped stream
8bb4bdeb
XL
908 // until a tag is found. Depending on its value, the appropriate content is copied
909 // to the wrapped stream.
910 // The copy from template is then resumed, repeating this sequence until reading
911 // the complete template.
912
913 loop {
914 let tag_length = match copy_and_capture(&mut tmplr, &mut self.writer, &mut tag_buf) {
915 None => return Ok(()),
916 Some(Err(e)) => return Err(Error::from(e)),
917 Some(Ok(val)) if val > 0 => val,
918 _ => continue,
919 };
920
041b39d2 921 debugln!("Help::write_template_help:iter: tag_buf={};", unsafe {
abe05a73
XL
922 String::from_utf8_unchecked(
923 tag_buf.get_ref()[0..tag_length]
924 .iter()
925 .map(|&i| i)
926 .collect::<Vec<_>>(),
927 )
8bb4bdeb
XL
928 });
929 match &tag_buf.get_ref()[0..tag_length] {
930 b"?" => {
3b2f2976 931 self.writer.write_all(b"Could not decode tag name")?;
8bb4bdeb
XL
932 }
933 b"bin" => {
3b2f2976 934 self.write_bin_name(parser)?;
8bb4bdeb
XL
935 }
936 b"version" => {
abe05a73
XL
937 write!(
938 self.writer,
939 "{}",
940 parser.meta.version.unwrap_or("unknown version")
941 )?;
8bb4bdeb
XL
942 }
943 b"author" => {
abe05a73
XL
944 write!(
945 self.writer,
946 "{}",
947 parser.meta.author.unwrap_or("unknown author")
948 )?;
8bb4bdeb
XL
949 }
950 b"about" => {
abe05a73
XL
951 write!(
952 self.writer,
953 "{}",
954 parser.meta.about.unwrap_or("unknown about")
955 )?;
8bb4bdeb 956 }
ea8adc8c 957 b"long-about" => {
abe05a73
XL
958 write!(
959 self.writer,
960 "{}",
961 parser.meta.long_about.unwrap_or("unknown about")
962 )?;
ea8adc8c 963 }
8bb4bdeb 964 b"usage" => {
3b2f2976 965 write!(self.writer, "{}", usage::create_usage_no_title(parser, &[]))?;
8bb4bdeb
XL
966 }
967 b"all-args" => {
3b2f2976 968 self.write_all_args(parser)?;
8bb4bdeb
XL
969 }
970 b"unified" => {
041b39d2
XL
971 let opts_flags = parser
972 .flags()
8bb4bdeb
XL
973 .map(as_arg_trait)
974 .chain(parser.opts().map(as_arg_trait));
3b2f2976 975 self.write_args(opts_flags)?;
8bb4bdeb
XL
976 }
977 b"flags" => {
3b2f2976 978 self.write_args(parser.flags().map(as_arg_trait))?;
8bb4bdeb
XL
979 }
980 b"options" => {
3b2f2976 981 self.write_args(parser.opts().map(as_arg_trait))?;
8bb4bdeb
XL
982 }
983 b"positionals" => {
3b2f2976 984 self.write_args(parser.positionals().map(as_arg_trait))?;
8bb4bdeb
XL
985 }
986 b"subcommands" => {
3b2f2976 987 self.write_subcommands(parser)?;
8bb4bdeb
XL
988 }
989 b"after-help" => {
abe05a73
XL
990 write!(
991 self.writer,
992 "{}",
993 parser.meta.more_help.unwrap_or("unknown after-help")
994 )?;
8bb4bdeb
XL
995 }
996 b"before-help" => {
abe05a73
XL
997 write!(
998 self.writer,
999 "{}",
1000 parser.meta.pre_help.unwrap_or("unknown before-help")
1001 )?;
8bb4bdeb
XL
1002 }
1003 // Unknown tag, write it back.
1004 r => {
3b2f2976
XL
1005 self.writer.write_all(b"{")?;
1006 self.writer.write_all(r)?;
1007 self.writer.write_all(b"}")?;
8bb4bdeb
XL
1008 }
1009 }
1010 }
1011 }
1012}
1013
041b39d2
XL
1014fn wrap_help(help: &str, avail_chars: usize) -> String {
1015 let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false);
1016 help.lines()
1017 .map(|line| wrapper.fill(line))
1018 .collect::<Vec<String>>()
1019 .join("\n")
1020}
1021
1022#[cfg(test)]
1023mod test {
1024 use super::wrap_help;
1025
1026 #[test]
1027 fn wrap_help_last_word() {
1028 let help = String::from("foo bar baz");
1029 assert_eq!(wrap_help(&help, 5), "foo\nbar\nbaz");
8bb4bdeb
XL
1030 }
1031}