more helpful help formatting

master
Daniel Kolesa 2017-05-17 20:56:01 +02:00
parent 4ac173901a
commit c8a22ab3a0
2 changed files with 132 additions and 59 deletions

View File

@ -16,6 +16,8 @@
#ifndef OSTD_ARGPARSE_HH #ifndef OSTD_ARGPARSE_HH
#define OSTD_ARGPARSE_HH #define OSTD_ARGPARSE_HH
#include <cctype>
#include <algorithm>
#include <optional> #include <optional>
#include <vector> #include <vector>
#include <memory> #include <memory>
@ -72,21 +74,32 @@ struct arg_argument: arg_description {
return p_helpstr; return p_helpstr;
} }
arg_argument &metavar(string_range str) {
p_metavar = std::string{str};
return *this;
}
string_range get_metavar() const {
return p_metavar;
}
std::size_t get_nargs() const {
return p_nargs;
}
protected: protected:
arg_argument(arg_value req = arg_value::NONE, int nargs = 1): arg_argument(arg_value req = arg_value::NONE, std::size_t nargs = 1):
arg_description(), p_valreq(req), p_nargs(nargs) arg_description(), p_valreq(req), p_nargs(nargs)
{} {}
arg_argument(int nargs): arg_argument(std::size_t nargs):
arg_description(), arg_description(),
p_valreq((nargs > 0) p_valreq((nargs > 0) ? arg_value::REQUIRED : arg_value::NONE),
? arg_value::REQUIRED p_nargs(nargs)
: ((nargs < 0) ? arg_value::ALL : arg_value::NONE)
), p_nargs(nargs)
{} {}
std::string p_helpstr; std::string p_helpstr, p_metavar;
arg_value p_valreq; arg_value p_valreq;
int p_nargs; std::size_t p_nargs;
}; };
struct arg_optional: arg_argument { struct arg_optional: arg_argument {
@ -138,26 +151,27 @@ struct arg_optional: arg_argument {
protected: protected:
arg_optional() = delete; arg_optional() = delete;
arg_optional(string_range name, arg_value req, int nargs = 1): arg_optional(string_range name, arg_value req, std::size_t nargs = 1):
arg_argument(req, nargs) arg_argument(req, nargs)
{ {
p_names.emplace_back(name); p_names.emplace_back(name);
} }
arg_optional(string_range name, int nargs): arg_optional(string_range name, std::size_t nargs):
arg_argument(nargs) arg_argument(nargs)
{ {
p_names.emplace_back(name); p_names.emplace_back(name);
} }
arg_optional( arg_optional(
string_range name1, string_range name2, arg_value req, int nargs = 1 string_range name1, string_range name2, arg_value req,
std::size_t nargs = 1
): ):
arg_argument(req, nargs) arg_argument(req, nargs)
{ {
p_names.emplace_back(name1); p_names.emplace_back(name1);
p_names.emplace_back(name2); p_names.emplace_back(name2);
} }
arg_optional(string_range name1, string_range name2, int nargs): arg_optional(string_range name1, string_range name2, std::size_t nargs):
arg_argument(nargs) arg_argument(nargs)
{ {
p_names.emplace_back(name1); p_names.emplace_back(name1);
@ -195,12 +209,13 @@ struct arg_positional: arg_argument {
protected: protected:
arg_positional() = delete; arg_positional() = delete;
arg_positional( arg_positional(
string_range name, arg_value req = arg_value::REQUIRED, int nargs = 1 string_range name, arg_value req = arg_value::REQUIRED,
std::size_t nargs = 1
): ):
arg_argument(req, nargs), arg_argument(req, nargs),
p_name(name) p_name(name)
{} {}
arg_positional(string_range name, int nargs): arg_positional(string_range name, std::size_t nargs):
arg_argument(nargs), arg_argument(nargs),
p_name(name) p_name(name)
{} {}
@ -454,49 +469,50 @@ struct default_help_formatter {
void format_options(OutputRange &out) { void format_options(OutputRange &out) {
std::size_t opt_namel = 0, pos_namel = 0; std::size_t opt_namel = 0, pos_namel = 0;
for (auto &p: p_parser.iter()) { for (auto &p: p_parser.iter()) {
auto cs = counting_sink(noop_sink<char>());
switch (p->type()) { switch (p->type()) {
case arg_type::OPTIONAL: { case arg_type::OPTIONAL: {
auto &opt = static_cast<arg_optional &>(*p); format_option(cs, static_cast<arg_optional &>(*p));
auto names = opt.get_names(); opt_namel = std::max(opt_namel, cs.get_written());
std::size_t nl = 0;
for (auto &s: names) {
nl += s.size();
}
nl += 2 * (names.size() - 1);
opt_namel = std::max(opt_namel, nl);
break; break;
} }
case arg_type::POSITIONAL: case arg_type::POSITIONAL:
pos_namel = std::max( format_option(cs, static_cast<arg_positional &>(*p));
pos_namel, pos_namel = std::max(pos_namel, cs.get_written());
static_cast<arg_positional &>(*p).get_name().size()
);
break; break;
default: default:
break; break;
} }
} }
std::size_t maxpad = std::max(opt_namel, pos_namel); std::size_t maxpad = std::max(opt_namel, pos_namel);
auto write_help = [maxpad](
auto &out, arg_argument &arg, std::size_t len
) {
auto help = arg.get_help();
if (help.empty()) {
out.put('\n');
} else {
std::size_t nd = maxpad - len + 2;
for (std::size_t i = 0; i < nd; ++i) {
out.put(' ');
}
format(out, "%s\n", help);
}
};
if (pos_namel) { if (pos_namel) {
format(out, "\npositional arguments:\n"); format(out, "\npositional arguments:\n");
for (auto &p: p_parser.iter()) { for (auto &p: p_parser.iter()) {
if (p->type() != arg_type::POSITIONAL) { if (p->type() != arg_type::POSITIONAL) {
continue; continue;
} }
format(out, " ");
auto &parg = static_cast<arg_positional &>(*p.get()); auto &parg = static_cast<arg_positional &>(*p.get());
auto name = parg.get_name(), help = parg.get_help(); auto cr = counting_sink(out);
format(out, " %s", name); format_option(cr, parg);
if (help.empty()) { out = std::move(cr.get_range());
out.put('\n'); write_help(out, parg, cr.get_written());
} else {
std::size_t nd = maxpad - name.size() + 2;
for (std::size_t i = 0; i < nd; ++i) {
out.put(' ');
}
format(out, "%s\n", help);
}
} }
} }
@ -506,32 +522,74 @@ struct default_help_formatter {
if (p->type() != arg_type::OPTIONAL) { if (p->type() != arg_type::OPTIONAL) {
continue; continue;
} }
auto &parg = static_cast<arg_optional &>(*p.get());
auto names = parg.get_names();
auto help = parg.get_help();
format(out, " "); format(out, " ");
std::size_t nd = 0; auto &oarg = static_cast<arg_optional &>(*p.get());
for (auto &s: names) { auto cr = counting_sink(out);
if (nd) { format_option(cr, oarg);
nd += 2; out = std::move(cr.get_range());
format(out, ", "); write_help(out, oarg, cr.get_written());
}
nd += s.size();
format(out, "%s", s);
}
if (help.empty()) {
out.put('\n');
} else {
nd = maxpad - nd + 2;
for (std::size_t i = 0; i < nd; ++i) {
out.put(' ');
}
format(out, "%s\n", help);
}
} }
} }
} }
template<typename OutputRange>
void format_option(OutputRange &out, arg_optional const &arg) {
auto names = arg.get_names();
std::string mt{arg.get_metavar()};
if (mt.empty()) {
for (auto &s: names) {
if (!starts_with(s, "--")) {
continue;
}
string_range mtr = s;
while (!mtr.empty() && (mtr.front() == '-')) {
mtr.pop_front();
}
mt = std::string{mtr};
break;
}
if (mt.empty()) {
mt = "VALUE";
} else {
std::transform(mt.begin(), mt.end(), mt.begin(), toupper);
}
}
bool first = true;
for (auto &s: names) {
if (!first) {
format(out, ", ");
}
format(out, s);
switch (arg.needs_value()) {
case arg_value::REQUIRED:
format(out, " %s", mt);
break;
case arg_value::OPTIONAL:
format(out, " [%s]", mt);
break;
case arg_value::ALL:
if (arg.get_nargs() > 0) {
format(out, " %s", mt);
} else {
format(out, " [%s]", mt);
}
break;
default:
break;
}
first = false;
}
}
template<typename OutputRange>
void format_option(OutputRange &out, arg_positional const &arg) {
auto mt = arg.get_metavar();
if (mt.empty()) {
mt = arg.get_name();
}
format(out, mt);
}
private: private:
basic_arg_parser<default_help_formatter> &p_parser; basic_arg_parser<default_help_formatter> &p_parser;
}; };

View File

@ -1095,6 +1095,14 @@ namespace detail {
size_t get_written() const { size_t get_written() const {
return p_written; return p_written;
} }
R &get_range() {
return p_range;
}
R const &get_range() const {
return p_range;
}
}; };
} }
@ -1109,6 +1117,13 @@ namespace detail {
* ~~~{.cc} * ~~~{.cc}
* range_size_t<R> get_written() const; * range_size_t<R> get_written() const;
* ~~~ * ~~~
*
* These methods are provided to retrieve the range:
*
* ~~~{.cc}
* R &get_range();
* R const &get_range() const;
* ~~~
*/ */
template<typename R> template<typename R>
inline auto counting_sink(R const &range) { inline auto counting_sink(R const &range) {