aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-11-06 10:57:00 +0300
committerPavel Belikov <pavel.fuchs.belikov@gmail.com>2017-11-06 10:57:00 +0300
commitff6f09803274b41d695e9e632db41b380d9e9c99 (patch)
tree6caa0241fc7b1f38f03815c74d0fd280a75989da
parentrefactor GetDescription (diff)
downloadargs.hxx-ff6f09803274b41d695e9e632db41b380d9e9c99.tar.xz
add description for commands
-rw-r--r--args.hxx90
-rw-r--r--test.cxx30
2 files changed, 112 insertions, 8 deletions
diff --git a/args.hxx b/args.hxx
index 53b8718..ca4d966 100644
--- a/args.hxx
+++ b/args.hxx
@@ -473,12 +473,20 @@ namespace args
*/
bool showProglinePositionals = true;
+ /** The prefix for short flags
+ */
std::string shortPrefix;
+ /** The prefix for long flags
+ */
std::string longPrefix;
+ /** The separator for short flags
+ */
std::string shortSeparator;
+ /** The separator for long flags
+ */
std::string longSeparator;
};
@@ -1081,10 +1089,11 @@ namespace args
std::vector<std::string> args;
std::vector<std::string> kicked;
ArgumentParser &parser;
+ Command &command;
bool isParsed = false;
public:
- Subparser(std::vector<std::string> args_, ArgumentParser &parser_) : args(std::move(args_)), parser(parser_)
+ Subparser(std::vector<std::string> args_, ArgumentParser &parser_, Command &command_) : args(std::move(args_)), parser(parser_), command(command_)
{
}
@@ -1093,6 +1102,11 @@ namespace args
Subparser &operator = (const Subparser&) = delete;
Subparser &operator = (Subparser&&) = delete;
+ Command &GetCommand()
+ {
+ return command;
+ }
+
bool IsParsed() const
{
return isParsed;
@@ -1109,10 +1123,13 @@ namespace args
class Command : public Group
{
private:
+ friend class Subparser;
+
std::string name;
std::string description;
std::function<void(Subparser&)> parserCoroutine;
bool commandIsRequired = false;
+ std::vector<std::tuple<std::string, std::string, unsigned>> subparserDescription;
protected:
@@ -1146,6 +1163,17 @@ namespace args
return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine;
}
+ Command &SelectedCommand()
+ {
+ Command *res = this;
+ while (res->selectedCommand != nullptr)
+ {
+ res = res->selectedCommand;
+ }
+
+ return *res;
+ }
+
public:
Command(Group &base_, std::string name_, std::string description_, std::function<void(Subparser&)> coroutine_ = {})
: name(std::move(name_)), description(std::move(description_)), parserCoroutine(std::move(coroutine_))
@@ -1300,6 +1328,54 @@ namespace args
return { this };
}
+ virtual std::vector<std::tuple<std::string, std::string, unsigned>> GetDescription(const HelpParams &params, const unsigned int indent) const
+ {
+ if (selectedCommand != nullptr)
+ {
+ return selectedCommand->GetDescription(params, indent);
+ }
+
+ std::vector<std::tuple<std::string, std::string, unsigned>> descriptions;
+ unsigned addindent = 0;
+
+ std::tuple<std::string, std::string, unsigned> desc;
+ std::get<0>(desc) = Name();
+ std::get<1>(desc) = description;
+ std::get<2>(desc) = indent;
+ if (!Name().empty())
+ {
+ addindent = 1;
+ descriptions.push_back(desc);
+ }
+
+ if (!Matched())
+ {
+ return descriptions;
+ }
+
+ for (Base *child: Children())
+ {
+ if ((child->GetOptions() & Options::Hidden) != Options::None)
+ {
+ continue;
+ }
+
+ auto groupDescriptions = child->GetDescription(params, indent + addindent);
+ descriptions.insert(
+ std::end(descriptions),
+ std::make_move_iterator(std::begin(groupDescriptions)),
+ std::make_move_iterator(std::end(groupDescriptions)));
+ }
+
+ for (auto childDescription: subparserDescription)
+ {
+ std::get<2>(childDescription) += indent + addindent;
+ descriptions.push_back(std::move(childDescription));
+ }
+
+ return descriptions;
+ }
+
virtual void Validate(const std::string &shortprefix, const std::string &longprefix) override
{
for (Base *child: Children())
@@ -1630,6 +1706,7 @@ namespace args
ShortPrefix("-");
LongSeparator("=");
SetArgumentSeparations(true, true, true, true);
+ matched = true;
}
/** The program name for help generation
@@ -1921,18 +1998,15 @@ namespace args
}
};
- Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) : command(&parser_), parser(std::move(args_), parser_)
+ Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector<std::string> args_) : command(&parser_), parser(std::move(args_), parser_, parser_.SelectedCommand())
{
- while (command->selectedCommand != nullptr)
- {
- command = command->selectedCommand;
- }
-
- command->subparser = &parser;
+ parser.GetCommand().subparser = &parser;
}
void Subparser::Parse()
{
+ isParsed = true;
+ command.subparserDescription = GetDescription(parser.helpParams, 0);
auto it = parser.Parse(args.begin(), args.end());
kicked.assign(it, args.end());
}
diff --git a/test.cxx b/test.cxx
index a9991fc..e95786e 100644
--- a/test.cxx
+++ b/test.cxx
@@ -746,6 +746,36 @@ TEST_CASE("Subparser commands with kick-out flags work as expected", "[args]")
REQUIRE((kickedOut == std::vector<std::string>{"A", "B", "C", "D"}));
}
+TEST_CASE("Subparser help works as expected", "[args]")
+{
+ args::ArgumentParser p("git-like parser");
+ args::Flag g(p, "GLOBAL", "global flag", {'g'}, args::Options::Global);
+
+ args::Command add(p, "add", "add file contents to the index", [&](args::Subparser &c)
+ {
+ args::Flag flag(c, "FLAG", "flag", {'f'});
+ c.Parse();
+ });
+
+ args::Command commit(p, "commit", "record changes to the repository", [&](args::Subparser &c)
+ {
+ args::Flag flag(c, "FLAG", "flag", {'f'});
+ c.Parse();
+ });
+
+ auto d = p.GetDescription(p.helpParams, 0);
+ REQUIRE(d.size() == 3);
+ REQUIRE(std::get<0>(d[0]) == "-g");
+ REQUIRE(std::get<0>(d[1]) == "add");
+ REQUIRE(std::get<0>(d[2]) == "commit");
+
+ p.ParseArgs(std::vector<std::string>{"add"});
+ d = p.GetDescription(p.helpParams, 0);
+ REQUIRE(d.size() == 2);
+ REQUIRE(std::get<0>(d[0]) == "add");
+ REQUIRE(std::get<0>(d[1]) == "-f");
+}
+
#undef ARGS_HXX
#define ARGS_TESTNAMESPACE
#define ARGS_NOEXCEPT