forked from OctaForge/libostd
more helpful help formatting
parent
4ac173901a
commit
c8a22ab3a0
176
ostd/argparse.hh
176
ostd/argparse.hh
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue