2016-09-05 19:29:40 +00:00
|
|
|
// Std
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
|
2016-09-05 19:29:40 +00:00
|
|
|
// Internal
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
use app::{App, AppSettings};
|
|
|
|
use app::parser::Parser;
|
2016-09-05 19:29:40 +00:00
|
|
|
use args::{AnyArg, ArgSettings, DispOrder};
|
|
|
|
use errors::{Error, Result as ClapResult};
|
2016-05-30 08:07:44 +00:00
|
|
|
use fmt::{Format, Colorizer};
|
2016-09-05 19:29:40 +00:00
|
|
|
use std::cmp;
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use std::fmt::Display;
|
|
|
|
use std::io::{self, Cursor, Read, Write};
|
|
|
|
use std::usize;
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-08-29 05:36:06 +00:00
|
|
|
#[cfg(feature = "wrap_help")]
|
2016-06-30 03:19:08 +00:00
|
|
|
use term_size;
|
2016-09-05 19:29:40 +00:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
|
|
|
|
|
|
|
use vec_map::VecMap;
|
2016-08-26 11:11:55 +00:00
|
|
|
#[cfg(not(feature = "wrap_help"))]
|
2016-06-30 03:19:08 +00:00
|
|
|
mod term_size {
|
|
|
|
pub fn dimensions() -> Option<(usize, usize)> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-04-10 00:35:24 +00:00
|
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
|
2016-08-25 22:31:55 +00:00
|
|
|
// use strext::_StrExt;
|
2016-04-10 00:35:24 +00:00
|
|
|
|
|
|
|
fn str_width(s: &str) -> usize {
|
|
|
|
UnicodeWidthStr::width(s)
|
|
|
|
}
|
|
|
|
|
2016-04-01 06:00:11 +00:00
|
|
|
const TAB: &'static str = " ";
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
// These are just convenient traits to make the code easier to read.
|
2016-04-01 06:00:11 +00:00
|
|
|
trait ArgWithDisplay<'b, 'c>: AnyArg<'b, 'c> + Display {}
|
|
|
|
impl<'b, 'c, T> ArgWithDisplay<'b, 'c> for T where T: AnyArg<'b, 'c> + Display {}
|
|
|
|
|
|
|
|
trait ArgWithOrder<'b, 'c>: ArgWithDisplay<'b, 'c> + DispOrder {
|
|
|
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c>;
|
|
|
|
}
|
|
|
|
impl<'b, 'c, T> ArgWithOrder<'b, 'c> for T
|
|
|
|
where T: ArgWithDisplay<'b, 'c> + DispOrder
|
|
|
|
{
|
|
|
|
fn as_base(&self) -> &ArgWithDisplay<'b, 'c> {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
fn as_arg_trait<'a, 'b, T: ArgWithOrder<'a, 'b>>(x: &T) -> &ArgWithOrder<'a, 'b> {
|
|
|
|
x
|
|
|
|
}
|
|
|
|
|
2016-04-01 06:00:11 +00:00
|
|
|
impl<'b, 'c> DispOrder for App<'b, 'c> {
|
|
|
|
fn disp_ord(&self) -> usize {
|
|
|
|
999
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-18 06:45:44 +00:00
|
|
|
macro_rules! color {
|
2016-05-30 08:07:44 +00:00
|
|
|
($_self:ident, $s:expr, $c:ident) => {
|
2016-04-18 06:45:44 +00:00
|
|
|
if $_self.color {
|
2016-05-30 08:07:44 +00:00
|
|
|
write!($_self.writer, "{}", $_self.cizer.$c($s))
|
2016-04-18 06:45:44 +00:00
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
write!($_self.writer, "{}", $s)
|
2016-04-18 06:45:44 +00:00
|
|
|
}
|
|
|
|
};
|
2016-05-30 08:07:44 +00:00
|
|
|
($_self:ident, $fmt_s:expr, $v:expr, $c:ident) => {
|
2016-04-18 06:45:44 +00:00
|
|
|
if $_self.color {
|
2016-05-30 08:07:44 +00:00
|
|
|
write!($_self.writer, "{}", $_self.cizer.$c(format!($fmt_s, $v)))
|
2016-04-18 06:45:44 +00:00
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
write!($_self.writer, $fmt_s, $v)
|
2016-04-18 06:45:44 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2016-04-03 04:20:55 +00:00
|
|
|
|
2016-05-14 20:25:00 +00:00
|
|
|
/// `clap` Help Writer.
|
2016-04-03 04:20:55 +00:00
|
|
|
///
|
2016-05-14 20:25:00 +00:00
|
|
|
/// Wraps a writer stream providing different methods to generate help for `clap` objects.
|
2016-04-01 06:00:11 +00:00
|
|
|
pub struct Help<'a> {
|
|
|
|
writer: &'a mut Write,
|
|
|
|
next_line_help: bool,
|
|
|
|
hide_pv: bool,
|
2016-06-14 01:45:24 +00:00
|
|
|
term_w: usize,
|
2016-04-18 06:45:44 +00:00
|
|
|
color: bool,
|
2016-05-30 08:07:44 +00:00
|
|
|
cizer: Colorizer,
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
// Public Functions
|
2016-04-01 06:00:11 +00:00
|
|
|
impl<'a> Help<'a> {
|
2016-05-14 20:25:00 +00:00
|
|
|
/// Create a new `Help` instance.
|
2016-09-05 19:29:40 +00:00
|
|
|
pub fn new(w: &'a mut Write,
|
|
|
|
next_line_help: bool,
|
|
|
|
hide_pv: bool,
|
|
|
|
color: bool,
|
|
|
|
cizer: Colorizer,
|
|
|
|
term_w: Option<usize>)
|
|
|
|
-> Self {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=Help::new;");
|
2016-04-01 06:00:11 +00:00
|
|
|
Help {
|
|
|
|
writer: w,
|
|
|
|
next_line_help: next_line_help,
|
|
|
|
hide_pv: hide_pv,
|
2016-06-14 01:45:24 +00:00
|
|
|
term_w: match term_w {
|
2016-09-05 19:29:40 +00:00
|
|
|
Some(width) => if width == 0 { usize::MAX } else { width },
|
|
|
|
None => term_size::dimensions().map_or(120, |(w, _)| w),
|
2016-06-14 01:45:24 +00:00
|
|
|
},
|
2016-04-18 06:45:44 +00:00
|
|
|
color: color,
|
2016-05-30 08:07:44 +00:00
|
|
|
cizer: cizer,
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Reads help settings from an App
|
|
|
|
/// and write its help to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
pub fn write_app_help(w: &'a mut Write, app: &App) -> ClapResult<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=Help::write_app_help;");
|
2016-04-10 00:35:24 +00:00
|
|
|
Self::write_parser_help(w, &app.p)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads help settings from a Parser
|
|
|
|
/// and write its help to the wrapped stream.
|
|
|
|
pub fn write_parser_help(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=Help::write_parser_help;");
|
|
|
|
Self::_write_parser_help(w, parser, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads help settings from a Parser
|
|
|
|
/// and write its help to the wrapped stream which will be stderr. This method prevents
|
|
|
|
/// formatting when required.
|
|
|
|
pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
|
|
|
|
debugln!("fn=Help::write_parser_help;");
|
|
|
|
Self::_write_parser_help(w, parser, true)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool) -> ClapResult<()> {
|
|
|
|
debugln!("fn=Help::write_parser_help;");
|
2016-04-10 00:35:24 +00:00
|
|
|
let nlh = parser.is_set(AppSettings::NextLineHelp);
|
|
|
|
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
|
2016-04-18 06:45:44 +00:00
|
|
|
let color = parser.is_set(AppSettings::ColoredHelp);
|
2016-05-30 08:07:44 +00:00
|
|
|
let cizer = Colorizer {
|
|
|
|
use_stderr: stderr,
|
|
|
|
when: parser.color(),
|
|
|
|
};
|
2016-06-14 01:45:24 +00:00
|
|
|
Self::new(w, nlh, hide_v, color, cizer, parser.meta.term_w).write_help(parser)
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes the parser help to the wrapped stream.
|
|
|
|
pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=Help::write_help;");
|
2016-04-03 04:20:55 +00:00
|
|
|
if let Some(h) = parser.meta.help_str {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(write!(self.writer, "{}", h).map_err(Error::from));
|
2016-08-20 21:33:58 +00:00
|
|
|
} else if let Some(tmpl) = parser.meta.template {
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(self.write_templated_help(&parser, tmpl));
|
|
|
|
} else {
|
|
|
|
try!(self.write_default_help(&parser));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
// Methods to write AnyArg help.
|
2016-04-01 06:00:11 +00:00
|
|
|
impl<'a> Help<'a> {
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes help for each argument in the order they were declared to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
|
|
|
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
|
|
|
{
|
|
|
|
let mut longest = 0;
|
|
|
|
let mut arg_v = Vec::with_capacity(10);
|
|
|
|
for arg in args.filter(|arg| {
|
|
|
|
!(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp)
|
|
|
|
}) {
|
|
|
|
if arg.longest_filter() {
|
|
|
|
longest = cmp::max(longest, arg.to_string().len());
|
|
|
|
}
|
|
|
|
if !arg.is_set(ArgSettings::Hidden) {
|
|
|
|
arg_v.push(arg)
|
|
|
|
}
|
|
|
|
}
|
2016-05-30 08:07:44 +00:00
|
|
|
let mut first = true;
|
2016-04-01 06:00:11 +00:00
|
|
|
for arg in arg_v {
|
2016-05-30 08:07:44 +00:00
|
|
|
if !first {
|
|
|
|
try!(self.writer.write(b"\n"));
|
|
|
|
} else {
|
|
|
|
first = false;
|
|
|
|
};
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_arg(arg.as_base(), longest));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Sorts arguments by length and display order and write their help to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
|
|
|
|
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
|
|
|
|
{
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=write_args;");
|
2016-04-01 06:00:11 +00:00
|
|
|
let mut longest = 0;
|
|
|
|
let mut ord_m = VecMap::new();
|
|
|
|
for arg in args.filter(|arg| {
|
|
|
|
!(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp)
|
|
|
|
}) {
|
|
|
|
if arg.longest_filter() {
|
|
|
|
longest = cmp::max(longest, arg.to_string().len());
|
|
|
|
}
|
|
|
|
if !arg.is_set(ArgSettings::Hidden) {
|
|
|
|
let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new());
|
|
|
|
btm.insert(arg.name(), arg);
|
|
|
|
}
|
|
|
|
}
|
2016-05-30 08:07:44 +00:00
|
|
|
let mut first = true;
|
2016-04-01 06:00:11 +00:00
|
|
|
for (_, btm) in ord_m.into_iter() {
|
|
|
|
for (_, arg) in btm.into_iter() {
|
2016-05-30 08:07:44 +00:00
|
|
|
if !first {
|
|
|
|
try!(self.writer.write(b"\n"));
|
|
|
|
} else {
|
|
|
|
first = false;
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_arg(arg.as_base(), longest));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes help for an argument to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn write_arg<'b, 'c>(&mut self,
|
|
|
|
arg: &ArgWithDisplay<'b, 'c>,
|
|
|
|
longest: usize)
|
|
|
|
-> io::Result<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=write_arg;");
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.short(arg));
|
|
|
|
try!(self.long(arg, longest));
|
|
|
|
try!(self.val(arg, longest));
|
|
|
|
try!(self.help(arg, longest));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes argument's short command to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn short<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>) -> io::Result<()> {
|
|
|
|
debugln!("fn=short;");
|
|
|
|
try!(write!(self.writer, "{}", TAB));
|
|
|
|
if let Some(s) = arg.short() {
|
2016-05-30 08:07:44 +00:00
|
|
|
color!(self, "-{}", s, good)
|
2016-04-01 06:00:11 +00:00
|
|
|
} else if arg.has_switch() {
|
|
|
|
write!(self.writer, "{}", TAB)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes argument's long command to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn long<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
|
|
|
debugln!("fn=long;");
|
|
|
|
if !arg.has_switch() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
if arg.takes_value() {
|
|
|
|
if let Some(l) = arg.long() {
|
2016-04-18 06:45:44 +00:00
|
|
|
if arg.short().is_some() {
|
|
|
|
try!(write!(self.writer, ", "));
|
|
|
|
}
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "--{}", l, good))
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
try!(write!(self.writer, " "));
|
2016-07-26 00:56:22 +00:00
|
|
|
} else if let Some(l) = arg.long() {
|
|
|
|
if arg.short().is_some() {
|
|
|
|
try!(write!(self.writer, ", "));
|
|
|
|
}
|
|
|
|
try!(color!(self, "--{}", l, good));
|
|
|
|
if !self.next_line_help || !arg.is_set(ArgSettings::NextLineHelp) {
|
|
|
|
write_nspaces!(self.writer, (longest + 4) - (l.len() + 2));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
2016-07-26 00:56:22 +00:00
|
|
|
} else if !self.next_line_help || !arg.is_set(ArgSettings::NextLineHelp) {
|
|
|
|
// 6 is tab (4) + -- (2)
|
|
|
|
write_nspaces!(self.writer, (longest + 6));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes argument's possible values to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn val<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
|
|
|
debugln!("fn=val;");
|
|
|
|
if !arg.takes_value() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
2016-08-20 21:33:58 +00:00
|
|
|
if let Some(vec) = arg.val_names() {
|
2016-04-01 06:00:11 +00:00
|
|
|
let mut it = vec.iter().peekable();
|
|
|
|
while let Some((_, val)) = it.next() {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "<{}>", val, good));
|
2016-04-01 06:00:11 +00:00
|
|
|
if it.peek().is_some() {
|
|
|
|
try!(write!(self.writer, " "));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let num = vec.len();
|
|
|
|
if arg.is_set(ArgSettings::Multiple) && num == 1 {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "...", good));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
} else if let Some(num) = arg.num_vals() {
|
|
|
|
let mut it = (0..num).peekable();
|
|
|
|
while let Some(_) = it.next() {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "<{}>", arg.name(), good));
|
2016-04-01 06:00:11 +00:00
|
|
|
if it.peek().is_some() {
|
|
|
|
try!(write!(self.writer, " "));
|
|
|
|
}
|
|
|
|
}
|
2016-04-10 00:35:24 +00:00
|
|
|
} else if arg.has_switch() {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "<{}>", arg.name(), good));
|
2016-04-01 06:00:11 +00:00
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "{}", arg, good));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
2016-08-28 03:42:31 +00:00
|
|
|
|
|
|
|
let spec_vals = self.spec_vals(arg);
|
|
|
|
let h = arg.help().unwrap_or("");
|
|
|
|
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
|
|
|
|
let width = self.term_w;
|
|
|
|
let taken = (longest + 12) + str_width(&*spec_vals);
|
2016-09-05 19:29:40 +00:00
|
|
|
let force_next_line = !nlh && width >= taken && str_width(h) > (width - taken) &&
|
|
|
|
(taken as f32 / width as f32) > 0.25;
|
2016-08-28 03:42:31 +00:00
|
|
|
|
2016-04-01 06:00:11 +00:00
|
|
|
if arg.has_switch() {
|
2016-08-28 03:42:31 +00:00
|
|
|
if !(nlh || force_next_line) {
|
2016-04-01 06:00:11 +00:00
|
|
|
let self_len = arg.to_string().len();
|
|
|
|
// subtract ourself
|
|
|
|
let mut spcs = longest - self_len;
|
|
|
|
// Since we're writing spaces from the tab point we first need to know if we
|
|
|
|
// had a long and short, or just short
|
|
|
|
if arg.long().is_some() {
|
|
|
|
// Only account 4 after the val
|
|
|
|
spcs += 4;
|
|
|
|
} else {
|
|
|
|
// Only account for ', --' + 4 after the val
|
|
|
|
spcs += 8;
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
write_nspaces!(self.writer, spcs);
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
2016-08-28 03:42:31 +00:00
|
|
|
} else if !(nlh || force_next_line) {
|
2016-04-03 04:20:55 +00:00
|
|
|
write_nspaces!(self.writer, longest + 4 - (arg.to_string().len()));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-07-26 00:56:22 +00:00
|
|
|
fn write_before_after_help(&mut self, h: &str) -> io::Result<()> {
|
2016-06-14 02:03:27 +00:00
|
|
|
debugln!("fn=before_help;");
|
|
|
|
let mut help = String::new();
|
|
|
|
// determine if our help fits or needs to wrap
|
|
|
|
let width = self.term_w;
|
|
|
|
debugln!("Term width...{}", width);
|
|
|
|
let too_long = str_width(h) >= width;
|
|
|
|
|
|
|
|
debug!("Too long...");
|
|
|
|
if too_long {
|
|
|
|
sdebugln!("Yes");
|
|
|
|
help.push_str(h);
|
|
|
|
debugln!("help: {}", help);
|
|
|
|
debugln!("help width: {}", str_width(&*help));
|
|
|
|
// Determine how many newlines we need to insert
|
|
|
|
debugln!("Usable space: {}", width);
|
|
|
|
let longest_w = {
|
|
|
|
let mut lw = 0;
|
|
|
|
for l in help.split(' ').map(|s| str_width(s)) {
|
|
|
|
if l > lw {
|
|
|
|
lw = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lw
|
|
|
|
};
|
2016-08-25 22:31:55 +00:00
|
|
|
wrap_help(&mut help, longest_w, width);
|
2016-06-14 02:03:27 +00:00
|
|
|
} else {
|
|
|
|
sdebugln!("No");
|
|
|
|
}
|
|
|
|
let help = if !help.is_empty() {
|
|
|
|
&*help
|
|
|
|
} else {
|
|
|
|
help.push_str(h);
|
|
|
|
&*help
|
|
|
|
};
|
|
|
|
if help.contains("{n}") {
|
|
|
|
if let Some(part) = help.split("{n}").next() {
|
|
|
|
try!(write!(self.writer, "{}", part));
|
|
|
|
}
|
|
|
|
for part in help.split("{n}").skip(1) {
|
|
|
|
try!(write!(self.writer, "\n{}", part));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try!(write!(self.writer, "{}", help));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes argument's help to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, longest: usize) -> io::Result<()> {
|
|
|
|
debugln!("fn=help;");
|
|
|
|
let spec_vals = self.spec_vals(arg);
|
|
|
|
let mut help = String::new();
|
|
|
|
let h = arg.help().unwrap_or("");
|
2016-08-28 03:42:31 +00:00
|
|
|
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
|
|
|
|
debugln!("Next Line...{:?}", nlh);
|
|
|
|
|
|
|
|
// determine if our help fits or needs to wrap
|
|
|
|
let width = self.term_w;
|
|
|
|
debugln!("Term width...{}", width);
|
|
|
|
|
|
|
|
// We calculate with longest+12 since if it's already NLH we don't care
|
|
|
|
let taken = (longest + 12) + str_width(&*spec_vals);
|
2016-09-05 19:29:40 +00:00
|
|
|
let force_next_line = !nlh && width >= taken && str_width(h) > (width - taken) &&
|
|
|
|
(taken as f32 / width as f32) > 0.25;
|
2016-08-28 03:42:31 +00:00
|
|
|
debugln!("Force Next Line...{:?}", force_next_line);
|
2016-09-05 19:29:40 +00:00
|
|
|
debugln!("Force Next Line math (help_len > (width - flags/opts/spcs))...{} > ({} - {})",
|
|
|
|
str_width(h),
|
|
|
|
width,
|
|
|
|
taken);
|
2016-08-28 03:42:31 +00:00
|
|
|
|
|
|
|
let spcs = if nlh || force_next_line {
|
2016-04-01 06:00:11 +00:00
|
|
|
8 // "tab" + "tab"
|
|
|
|
} else {
|
|
|
|
longest + 12
|
|
|
|
};
|
2016-08-28 03:42:31 +00:00
|
|
|
|
2016-06-14 01:45:24 +00:00
|
|
|
let too_long = spcs + str_width(h) + str_width(&*spec_vals) >= width;
|
2016-08-25 22:31:55 +00:00
|
|
|
debugln!("Spaces: {}", spcs);
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
// Is help on next line, if so newline + 2x tab
|
2016-08-28 03:42:31 +00:00
|
|
|
if nlh || force_next_line {
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(write!(self.writer, "\n{}{}", TAB, TAB));
|
|
|
|
}
|
|
|
|
|
|
|
|
debug!("Too long...");
|
2016-08-25 22:31:55 +00:00
|
|
|
if too_long && spcs <= width {
|
2016-04-01 06:00:11 +00:00
|
|
|
sdebugln!("Yes");
|
|
|
|
help.push_str(h);
|
|
|
|
help.push_str(&*spec_vals);
|
|
|
|
debugln!("help: {}", help);
|
2016-04-10 00:35:24 +00:00
|
|
|
debugln!("help width: {}", str_width(&*help));
|
2016-04-01 06:00:11 +00:00
|
|
|
// Determine how many newlines we need to insert
|
|
|
|
let avail_chars = width - spcs;
|
|
|
|
debugln!("Usable space: {}", avail_chars);
|
|
|
|
let longest_w = {
|
|
|
|
let mut lw = 0;
|
2016-04-10 00:35:24 +00:00
|
|
|
for l in help.split(' ').map(|s| str_width(s)) {
|
2016-04-01 06:00:11 +00:00
|
|
|
if l > lw {
|
|
|
|
lw = l;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lw
|
|
|
|
};
|
2016-08-25 22:31:55 +00:00
|
|
|
wrap_help(&mut help, longest_w, avail_chars);
|
2016-04-01 06:00:11 +00:00
|
|
|
} else {
|
|
|
|
sdebugln!("No");
|
|
|
|
}
|
|
|
|
let help = if !help.is_empty() {
|
|
|
|
&*help
|
2016-04-03 04:20:55 +00:00
|
|
|
} else if spec_vals.is_empty() {
|
|
|
|
h
|
|
|
|
} else {
|
2016-04-01 06:00:11 +00:00
|
|
|
help.push_str(h);
|
|
|
|
help.push_str(&*spec_vals);
|
|
|
|
&*help
|
|
|
|
};
|
|
|
|
if help.contains("{n}") {
|
|
|
|
if let Some(part) = help.split("{n}").next() {
|
|
|
|
try!(write!(self.writer, "{}", part));
|
|
|
|
}
|
|
|
|
for part in help.split("{n}").skip(1) {
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(write!(self.writer, "\n"));
|
2016-08-28 03:42:31 +00:00
|
|
|
if nlh || force_next_line {
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(write!(self.writer, "{}{}", TAB, TAB));
|
2016-04-01 06:00:11 +00:00
|
|
|
} else if arg.has_switch() {
|
2016-04-03 04:20:55 +00:00
|
|
|
write_nspaces!(self.writer, longest + 12);
|
2016-04-01 06:00:11 +00:00
|
|
|
} else {
|
2016-04-03 04:20:55 +00:00
|
|
|
write_nspaces!(self.writer, longest + 8);
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(write!(self.writer, "{}", part));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
try!(write!(self.writer, "{}", help));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn spec_vals(&self, a: &ArgWithDisplay) -> String {
|
|
|
|
debugln!("fn=spec_vals;");
|
2016-08-20 21:33:58 +00:00
|
|
|
if let Some(pv) = a.default_val() {
|
2016-04-01 06:00:11 +00:00
|
|
|
debugln!("Writing defaults");
|
|
|
|
return format!(" [default: {}] {}",
|
2016-05-06 21:35:53 +00:00
|
|
|
if self.color {
|
2016-05-30 08:07:44 +00:00
|
|
|
self.cizer.good(pv)
|
2016-05-06 21:35:53 +00:00
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
Format::None(pv)
|
2016-05-06 21:35:53 +00:00
|
|
|
},
|
|
|
|
if self.hide_pv {
|
2016-04-03 04:20:55 +00:00
|
|
|
"".into()
|
2016-05-06 21:35:53 +00:00
|
|
|
} else {
|
|
|
|
if let Some(ref pv) = a.possible_vals() {
|
|
|
|
if self.color {
|
|
|
|
format!(" [values: {}]",
|
|
|
|
pv.iter()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(|v| format!("{}", self.cizer.good(v)))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", "))
|
2016-05-06 21:35:53 +00:00
|
|
|
} else {
|
|
|
|
format!(" [values: {}]", pv.join(", "))
|
|
|
|
}
|
|
|
|
} else {
|
2016-04-01 06:00:11 +00:00
|
|
|
"".into()
|
2016-05-06 21:35:53 +00:00
|
|
|
}
|
|
|
|
});
|
2016-06-13 01:50:31 +00:00
|
|
|
} else if let Some(ref aliases) = a.aliases() {
|
|
|
|
debugln!("Writing aliases");
|
|
|
|
return format!(" [aliases: {}]",
|
2016-09-05 19:29:40 +00:00
|
|
|
if self.color {
|
|
|
|
aliases.iter()
|
|
|
|
.map(|v| format!("{}", self.cizer.good(v)))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", ")
|
|
|
|
} else {
|
|
|
|
aliases.join(", ")
|
|
|
|
});
|
2016-04-01 06:00:11 +00:00
|
|
|
} else if !self.hide_pv {
|
|
|
|
debugln!("Writing values");
|
2016-08-20 21:33:58 +00:00
|
|
|
if let Some(pv) = a.possible_vals() {
|
2016-04-01 06:00:11 +00:00
|
|
|
debugln!("Possible vals...{:?}", pv);
|
2016-05-06 21:35:53 +00:00
|
|
|
return if self.color {
|
|
|
|
format!(" [values: {}]",
|
|
|
|
pv.iter()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(|v| format!("{}", self.cizer.good(v)))
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(", "))
|
2016-05-06 21:35:53 +00:00
|
|
|
} else {
|
|
|
|
format!(" [values: {}]", pv.join(", "))
|
|
|
|
};
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
String::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
// Methods to write Parser help.
|
2016-04-01 06:00:11 +00:00
|
|
|
impl<'a> Help<'a> {
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes help for all arguments (options, flags, args, subcommands)
|
|
|
|
/// including titles of a Parser Object to the wrapped stream.
|
2016-06-08 04:10:56 +00:00
|
|
|
#[cfg_attr(feature = "lints", allow(useless_let_if_seq))]
|
2016-04-01 06:00:11 +00:00
|
|
|
pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
|
|
|
|
|
2016-04-09 18:25:21 +00:00
|
|
|
let flags = parser.has_flags();
|
|
|
|
let pos = parser.has_positionals();
|
|
|
|
let opts = parser.has_opts();
|
|
|
|
let subcmds = parser.has_subcommands();
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
let mut first = true;
|
|
|
|
|
2016-04-01 06:00:11 +00:00
|
|
|
if unified_help && (flags || opts) {
|
|
|
|
let opts_flags = parser.iter_flags()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)
|
|
|
|
.chain(parser.iter_opts().map(as_arg_trait));
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "OPTIONS:\n", warning));
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_args(opts_flags));
|
2016-05-30 09:39:29 +00:00
|
|
|
first = false;
|
2016-04-01 06:00:11 +00:00
|
|
|
} else {
|
|
|
|
if flags {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "FLAGS:\n", warning));
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_args(parser.iter_flags()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)));
|
2016-05-30 09:39:29 +00:00
|
|
|
first = false;
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
if opts {
|
2016-04-03 04:20:55 +00:00
|
|
|
if !first {
|
2016-05-30 09:39:29 +00:00
|
|
|
try!(self.writer.write(b"\n\n"));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "OPTIONS:\n", warning));
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_args(parser.iter_opts().map(as_arg_trait)));
|
2016-05-30 09:39:29 +00:00
|
|
|
first = false;
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if pos {
|
2016-04-03 04:20:55 +00:00
|
|
|
if !first {
|
2016-05-30 09:39:29 +00:00
|
|
|
try!(self.writer.write(b"\n\n"));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
2016-05-30 09:39:29 +00:00
|
|
|
try!(color!(self, "ARGS:\n", warning));
|
2016-04-01 06:00:11 +00:00
|
|
|
try!(self.write_args_unsorted(parser.iter_positionals().map(as_arg_trait)));
|
2016-05-30 09:39:29 +00:00
|
|
|
first = false;
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if subcmds {
|
2016-05-30 09:39:29 +00:00
|
|
|
if !first {
|
|
|
|
try!(self.writer.write(b"\n\n"));
|
|
|
|
}
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "SUBCOMMANDS:\n", warning));
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(self.write_subcommands(&parser));
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes help for subcommands of a Parser Object to the wrapped stream.
|
|
|
|
fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> {
|
2016-06-05 05:08:42 +00:00
|
|
|
debugln!("exec=write_subcommands;");
|
2016-04-03 04:20:55 +00:00
|
|
|
let mut longest = 0;
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
let mut ord_m = VecMap::new();
|
|
|
|
for sc in parser.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) {
|
|
|
|
let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new());
|
|
|
|
longest = cmp::max(longest, sc.p.meta.name.len());
|
2016-06-10 01:30:52 +00:00
|
|
|
btm.insert(sc.p.meta.name.clone(), sc.clone());
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
2016-05-30 08:07:44 +00:00
|
|
|
let mut first = true;
|
2016-04-03 04:20:55 +00:00
|
|
|
for (_, btm) in ord_m.into_iter() {
|
|
|
|
for (_, sc) in btm.into_iter() {
|
2016-05-30 08:07:44 +00:00
|
|
|
if !first {
|
|
|
|
try!(self.writer.write(b"\n"));
|
|
|
|
} else {
|
|
|
|
first = false;
|
|
|
|
}
|
2016-06-10 01:30:52 +00:00
|
|
|
try!(self.write_arg(&sc, longest));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes version of a Parser Object to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn write_version(&mut self, parser: &Parser) -> io::Result<()> {
|
|
|
|
try!(write!(self.writer, "{}", parser.meta.version.unwrap_or("".into())));
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes binary name of a Parser Object to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
fn write_bin_name(&mut self, parser: &Parser) -> io::Result<()> {
|
|
|
|
if let Some(bn) = parser.meta.bin_name.as_ref() {
|
|
|
|
if bn.contains(' ') {
|
|
|
|
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, bn.replace(" ", "-"), good))
|
2016-04-01 06:00:11 +00:00
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, &parser.meta.name[..], good))
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, &parser.meta.name[..], good))
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Writes default help for a Parser Object to the wrapped stream.
|
2016-04-01 06:00:11 +00:00
|
|
|
pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=write_default_help;");
|
|
|
|
if let Some(h) = parser.meta.pre_help {
|
2016-06-14 02:03:27 +00:00
|
|
|
try!(self.write_before_after_help(h));
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(self.writer.write(b"\n\n"));
|
|
|
|
}
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
// Print the version
|
|
|
|
try!(self.write_bin_name(&parser));
|
|
|
|
try!(self.writer.write(b" "));
|
|
|
|
try!(self.write_version(&parser));
|
|
|
|
try!(self.writer.write(b"\n"));
|
|
|
|
if let Some(author) = parser.meta.author {
|
|
|
|
try!(write!(self.writer, "{}\n", author));
|
|
|
|
}
|
|
|
|
if let Some(about) = parser.meta.about {
|
|
|
|
try!(write!(self.writer, "{}\n", about));
|
|
|
|
}
|
|
|
|
|
2016-05-30 08:07:44 +00:00
|
|
|
try!(color!(self, "\nUSAGE:", warning));
|
2016-05-06 21:35:53 +00:00
|
|
|
try!(write!(self.writer,
|
|
|
|
"\n{}{}\n\n",
|
2016-04-03 04:20:55 +00:00
|
|
|
TAB,
|
|
|
|
parser.create_usage_no_title(&[])));
|
2016-04-01 06:00:11 +00:00
|
|
|
|
2016-04-09 18:25:21 +00:00
|
|
|
let flags = parser.has_flags();
|
|
|
|
let pos = parser.has_positionals();
|
|
|
|
let opts = parser.has_opts();
|
|
|
|
let subcmds = parser.has_subcommands();
|
2016-04-01 06:00:11 +00:00
|
|
|
|
|
|
|
if flags || opts || pos || subcmds {
|
|
|
|
try!(self.write_all_args(&parser));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(h) = parser.meta.more_help {
|
2016-05-30 08:07:44 +00:00
|
|
|
if flags || opts || pos || subcmds {
|
|
|
|
try!(self.writer.write(b"\n\n"));
|
|
|
|
}
|
2016-06-14 02:03:27 +00:00
|
|
|
try!(self.write_before_after_help(h));
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.writer.flush().map_err(Error::from)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 04:20:55 +00:00
|
|
|
/// Possible results for a copying function that stops when a given
|
|
|
|
/// byte was found.
|
|
|
|
enum CopyUntilResult {
|
|
|
|
DelimiterFound(usize),
|
|
|
|
DelimiterNotFound(usize),
|
|
|
|
ReaderEmpty,
|
|
|
|
ReadError(io::Error),
|
|
|
|
WriteError(io::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copies the contents of a reader into a writer until a delimiter byte is found.
|
|
|
|
/// On success, the total number of bytes that were
|
|
|
|
/// copied from reader to writer is returned.
|
|
|
|
fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> CopyUntilResult {
|
|
|
|
|
|
|
|
let mut count = 0;
|
|
|
|
for wb in r.bytes() {
|
|
|
|
match wb {
|
|
|
|
Ok(b) => {
|
|
|
|
if b == delimiter_byte {
|
|
|
|
return CopyUntilResult::DelimiterFound(count);
|
|
|
|
}
|
|
|
|
match w.write(&[b]) {
|
|
|
|
Ok(c) => count += c,
|
|
|
|
Err(e) => return CopyUntilResult::WriteError(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => return CopyUntilResult::ReadError(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if count > 0 {
|
|
|
|
CopyUntilResult::DelimiterNotFound(count)
|
|
|
|
} else {
|
|
|
|
CopyUntilResult::ReaderEmpty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Copies the contents of a reader into a writer until a {tag} is found,
|
|
|
|
/// copying the tag content to a buffer and returning its size.
|
2016-05-30 08:07:44 +00:00
|
|
|
/// In addition to errors, there are three possible outputs:
|
2016-04-03 04:20:55 +00:00
|
|
|
/// - None: The reader was consumed.
|
|
|
|
/// - Some(Ok(0)): No tag was captured but the reader still contains data.
|
|
|
|
/// - Some(Ok(length>0)): a tag with `length` was captured to the tag_buffer.
|
|
|
|
fn copy_and_capture<R: Read, W: Write>(r: &mut R,
|
|
|
|
w: &mut W,
|
|
|
|
tag_buffer: &mut Cursor<Vec<u8>>)
|
|
|
|
-> Option<io::Result<usize>> {
|
|
|
|
use self::CopyUntilResult::*;
|
|
|
|
|
|
|
|
// Find the opening byte.
|
|
|
|
match copy_until(r, w, b'{') {
|
|
|
|
|
|
|
|
// The end of the reader was reached without finding the opening tag.
|
|
|
|
// (either with or without having copied data to the writer)
|
|
|
|
// Return None indicating that we are done.
|
2016-05-06 21:35:53 +00:00
|
|
|
ReaderEmpty |
|
|
|
|
DelimiterNotFound(_) => None,
|
2016-04-03 04:20:55 +00:00
|
|
|
|
|
|
|
// Something went wrong.
|
|
|
|
ReadError(e) | WriteError(e) => Some(Err(e)),
|
|
|
|
|
|
|
|
// The opening byte was found.
|
|
|
|
// (either with or without having copied data to the writer)
|
|
|
|
DelimiterFound(_) => {
|
|
|
|
|
|
|
|
// Lets reset the buffer first and find out how long it is.
|
|
|
|
tag_buffer.set_position(0);
|
|
|
|
let buffer_size = tag_buffer.get_ref().len();
|
|
|
|
|
|
|
|
// Find the closing byte,limiting the reader to the length of the buffer.
|
|
|
|
let mut rb = r.take(buffer_size as u64);
|
|
|
|
match copy_until(&mut rb, tag_buffer, b'}') {
|
|
|
|
|
|
|
|
// We were already at the end of the reader.
|
|
|
|
// Return None indicating that we are done.
|
|
|
|
ReaderEmpty => None,
|
|
|
|
|
|
|
|
// The closing tag was found.
|
|
|
|
// Return the tag_length.
|
|
|
|
DelimiterFound(tag_length) => Some(Ok(tag_length)),
|
|
|
|
|
|
|
|
// The end of the reader was found without finding the closing tag.
|
|
|
|
// Write the opening byte and captured text to the writer.
|
|
|
|
// Return 0 indicating that nothing was caputred but the reader still contains data.
|
|
|
|
DelimiterNotFound(not_tag_length) => {
|
|
|
|
match w.write(b"{") {
|
|
|
|
Err(e) => Some(Err(e)),
|
|
|
|
_ => {
|
|
|
|
match w.write(&tag_buffer.get_ref()[0..not_tag_length]) {
|
|
|
|
Err(e) => Some(Err(e)),
|
|
|
|
_ => Some(Ok(0)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReadError(e) | WriteError(e) => Some(Err(e)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Methods to write Parser help using templates.
|
|
|
|
impl<'a> Help<'a> {
|
|
|
|
/// Write help to stream for the parser in the format defined by the template.
|
|
|
|
///
|
|
|
|
/// Tags arg given inside curly brackets:
|
|
|
|
/// Valid tags are:
|
|
|
|
/// * `{bin}` - Binary name.
|
|
|
|
/// * `{version}` - Version number.
|
|
|
|
/// * `{author}` - Author information.
|
|
|
|
/// * `{usage}` - Automatically generated or given usage string.
|
|
|
|
/// * `{all-args}` - Help for all arguments (options, flags, positionals arguments,
|
|
|
|
/// and subcommands) including titles.
|
|
|
|
/// * `{unified}` - Unified help for options and flags.
|
|
|
|
/// * `{flags}` - Help for flags.
|
|
|
|
/// * `{options}` - Help for options.
|
|
|
|
/// * `{positionals}` - Help for positionals arguments.
|
|
|
|
/// * `{subcommands}` - Help for subcommands.
|
2016-05-02 18:25:05 +00:00
|
|
|
/// * `{after-help}` - Info to be displayed after the help message.
|
|
|
|
/// * `{before-help}` - Info to be displayed before the help message.
|
2016-04-03 04:20:55 +00:00
|
|
|
///
|
|
|
|
/// The template system is, on purpose, very simple. Therefore the tags have to writen
|
|
|
|
/// in the lowercase and without spacing.
|
|
|
|
fn write_templated_help(&mut self, parser: &Parser, template: &str) -> ClapResult<()> {
|
2016-05-30 08:07:44 +00:00
|
|
|
debugln!("fn=write_templated_help;");
|
2016-04-03 04:20:55 +00:00
|
|
|
let mut tmplr = Cursor::new(&template);
|
|
|
|
let mut tag_buf = Cursor::new(vec![0u8; 15]);
|
|
|
|
|
|
|
|
// The strategy is to copy the template from the the reader to wrapped stream
|
|
|
|
// until a tag is found. Depending on its value, the appropriate content is copied
|
|
|
|
// to the wrapped stream.
|
|
|
|
// The copy from template is then resumed, repeating this sequence until reading
|
|
|
|
// the complete template.
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let tag_length = match copy_and_capture(&mut tmplr, &mut self.writer, &mut tag_buf) {
|
|
|
|
None => return Ok(()),
|
|
|
|
Some(Err(e)) => return Err(Error::from(e)),
|
|
|
|
Some(Ok(val)) if val > 0 => val,
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
|
2016-09-05 19:29:40 +00:00
|
|
|
debugln!("iter;tag_buf={};", unsafe {
|
|
|
|
String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length]
|
|
|
|
.iter()
|
|
|
|
.map(|&i| i)
|
|
|
|
.collect::<Vec<_>>())
|
2016-05-30 09:39:29 +00:00
|
|
|
});
|
2016-04-03 04:20:55 +00:00
|
|
|
match &tag_buf.get_ref()[0..tag_length] {
|
|
|
|
b"?" => {
|
|
|
|
try!(self.writer.write(b"Could not decode tag name"));
|
|
|
|
}
|
|
|
|
b"bin" => {
|
|
|
|
try!(self.write_bin_name(&parser));
|
|
|
|
}
|
|
|
|
b"version" => {
|
|
|
|
try!(write!(self.writer,
|
|
|
|
"{}",
|
|
|
|
parser.meta.version.unwrap_or("unknown version")));
|
|
|
|
}
|
|
|
|
b"author" => {
|
|
|
|
try!(write!(self.writer,
|
|
|
|
"{}",
|
|
|
|
parser.meta.author.unwrap_or("unknown author")));
|
|
|
|
}
|
|
|
|
b"about" => {
|
|
|
|
try!(write!(self.writer,
|
|
|
|
"{}",
|
|
|
|
parser.meta.about.unwrap_or("unknown about")));
|
|
|
|
}
|
|
|
|
b"usage" => {
|
|
|
|
try!(write!(self.writer, "{}", parser.create_usage_no_title(&[])));
|
|
|
|
}
|
|
|
|
b"all-args" => {
|
|
|
|
try!(self.write_all_args(&parser));
|
|
|
|
}
|
|
|
|
b"unified" => {
|
|
|
|
let opts_flags = parser.iter_flags()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)
|
|
|
|
.chain(parser.iter_opts().map(as_arg_trait));
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(self.write_args(opts_flags));
|
|
|
|
}
|
|
|
|
b"flags" => {
|
|
|
|
try!(self.write_args(parser.iter_flags()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
|
|
|
b"options" => {
|
|
|
|
try!(self.write_args(parser.iter_opts()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
|
|
|
b"positionals" => {
|
|
|
|
try!(self.write_args(parser.iter_positionals()
|
2016-09-05 19:29:40 +00:00
|
|
|
.map(as_arg_trait)));
|
2016-04-03 04:20:55 +00:00
|
|
|
}
|
|
|
|
b"subcommands" => {
|
|
|
|
try!(self.write_subcommands(&parser));
|
|
|
|
}
|
|
|
|
b"after-help" => {
|
|
|
|
try!(write!(self.writer,
|
|
|
|
"{}",
|
|
|
|
parser.meta.more_help.unwrap_or("unknown after-help")));
|
|
|
|
}
|
2016-05-02 18:25:05 +00:00
|
|
|
b"before-help" => {
|
|
|
|
try!(write!(self.writer,
|
|
|
|
"{}",
|
|
|
|
parser.meta.pre_help.unwrap_or("unknown before-help")));
|
|
|
|
}
|
2016-04-03 04:20:55 +00:00
|
|
|
// Unknown tag, write it back.
|
2016-08-20 21:33:58 +00:00
|
|
|
r => {
|
2016-04-03 04:20:55 +00:00
|
|
|
try!(self.writer.write(b"{"));
|
|
|
|
try!(self.writer.write(r));
|
|
|
|
try!(self.writer.write(b"}"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-28 03:42:31 +00:00
|
|
|
#[cfg_attr(feature = "lints", allow(explicit_counter_loop))]
|
2016-08-25 22:31:55 +00:00
|
|
|
fn wrap_help(help: &mut String, longest_w: usize, avail_chars: usize) {
|
2016-09-05 19:29:40 +00:00
|
|
|
debugln!("fn=wrap_help;longest_w={},avail_chars={}",
|
|
|
|
longest_w,
|
|
|
|
avail_chars);
|
2016-08-25 22:31:55 +00:00
|
|
|
debug!("Enough space to wrap...");
|
|
|
|
if longest_w < avail_chars {
|
|
|
|
sdebugln!("Yes");
|
|
|
|
let mut prev_space = 0;
|
|
|
|
let mut j = 0;
|
|
|
|
let mut i = 0;
|
|
|
|
for (idx, g) in (&*help.clone()).grapheme_indices(true) {
|
|
|
|
debugln!("iter;idx={},g={}", idx, g);
|
2016-09-05 19:29:40 +00:00
|
|
|
if g != " " {
|
|
|
|
continue;
|
|
|
|
}
|
2016-08-25 22:31:55 +00:00
|
|
|
if str_width(&help[j..idx + (2 * i)]) < avail_chars {
|
2016-09-05 19:29:40 +00:00
|
|
|
debugln!("Still enough space...");
|
2016-08-25 22:31:55 +00:00
|
|
|
prev_space = idx;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
debugln!("Adding Newline...");
|
|
|
|
j = prev_space + (2 * i);
|
|
|
|
debugln!("i={},prev_space={},j={}", i, prev_space, j);
|
|
|
|
debugln!("removing: {}", j);
|
|
|
|
debugln!("char at {}: {}", j, &help[j..j]);
|
|
|
|
help.remove(j);
|
|
|
|
help.insert(j, '{');
|
|
|
|
help.insert(j + 1, 'n');
|
|
|
|
help.insert(j + 2, '}');
|
|
|
|
i += 1;
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
2016-08-25 22:31:55 +00:00
|
|
|
} else {
|
|
|
|
sdebugln!("No");
|
2016-04-01 06:00:11 +00:00
|
|
|
}
|
|
|
|
}
|