Make '--help' do the same as 'help' (i.e. show a manpage)
This commit is contained in:
parent
488a826842
commit
b159d23800
|
@ -96,41 +96,6 @@ void Args::parseCmdline(const Strings & _cmdline)
|
||||||
processArgs(pendingArgs, true);
|
processArgs(pendingArgs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Args::printHelp(const string & programName, std::ostream & out)
|
|
||||||
{
|
|
||||||
std::cout << fmt(ANSI_BOLD "Usage:" ANSI_NORMAL " %s " ANSI_ITALIC "FLAGS..." ANSI_NORMAL, programName);
|
|
||||||
for (auto & exp : expectedArgs) {
|
|
||||||
std::cout << renderLabels({exp.label});
|
|
||||||
// FIXME: handle arity > 1
|
|
||||||
if (exp.handler.arity == ArityAny) std::cout << "...";
|
|
||||||
if (exp.optional) std::cout << "?";
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
|
|
||||||
auto s = description();
|
|
||||||
if (s != "")
|
|
||||||
std::cout << "\n" ANSI_BOLD "Summary:" ANSI_NORMAL " " << s << ".\n";
|
|
||||||
|
|
||||||
if (longFlags.size()) {
|
|
||||||
std::cout << "\n";
|
|
||||||
std::cout << ANSI_BOLD "Flags:" ANSI_NORMAL "\n";
|
|
||||||
printFlags(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Args::printFlags(std::ostream & out)
|
|
||||||
{
|
|
||||||
Table2 table;
|
|
||||||
for (auto & flag : longFlags) {
|
|
||||||
if (hiddenCategories.count(flag.second->category)) continue;
|
|
||||||
table.push_back(std::make_pair(
|
|
||||||
(flag.second->shortName ? std::string("-") + flag.second->shortName + ", " : " ")
|
|
||||||
+ "--" + flag.first + renderLabels(flag.second->labels),
|
|
||||||
flag.second->description));
|
|
||||||
}
|
|
||||||
printTable(out, table);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
{
|
{
|
||||||
assert(pos != end);
|
assert(pos != end);
|
||||||
|
@ -331,28 +296,6 @@ Strings argvToStrings(int argc, char * * argv)
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string renderLabels(const Strings & labels)
|
|
||||||
{
|
|
||||||
std::string res;
|
|
||||||
for (auto label : labels) {
|
|
||||||
for (auto & c : label) c = std::toupper(c);
|
|
||||||
res += " " ANSI_ITALIC + label + ANSI_NORMAL;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printTable(std::ostream & out, const Table2 & table)
|
|
||||||
{
|
|
||||||
size_t max = 0;
|
|
||||||
for (auto & row : table)
|
|
||||||
max = std::max(max, filterANSIEscapes(row.first, true).size());
|
|
||||||
for (auto & row : table) {
|
|
||||||
out << " " << row.first
|
|
||||||
<< std::string(max - filterANSIEscapes(row.first, true).size() + 2, ' ')
|
|
||||||
<< row.second << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiCommand::MultiCommand(const Commands & commands)
|
MultiCommand::MultiCommand(const Commands & commands)
|
||||||
: commands(commands)
|
: commands(commands)
|
||||||
{
|
{
|
||||||
|
@ -376,38 +319,6 @@ MultiCommand::MultiCommand(const Commands & commands)
|
||||||
categories[Command::catDefault] = "Available commands";
|
categories[Command::catDefault] = "Available commands";
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiCommand::printHelp(const string & programName, std::ostream & out)
|
|
||||||
{
|
|
||||||
if (command) {
|
|
||||||
command->second->printHelp(programName + " " + command->first, out);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out << fmt(ANSI_BOLD "Usage:" ANSI_NORMAL " %s " ANSI_ITALIC "COMMAND FLAGS... ARGS..." ANSI_NORMAL "\n", programName);
|
|
||||||
|
|
||||||
out << "\n" ANSI_BOLD "Common flags:" ANSI_NORMAL "\n";
|
|
||||||
printFlags(out);
|
|
||||||
|
|
||||||
std::map<Command::Category, std::map<std::string, ref<Command>>> commandsByCategory;
|
|
||||||
|
|
||||||
for (auto & [name, commandFun] : commands) {
|
|
||||||
auto command = commandFun();
|
|
||||||
commandsByCategory[command->category()].insert_or_assign(name, command);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto & [category, commands] : commandsByCategory) {
|
|
||||||
out << fmt("\n" ANSI_BOLD "%s:" ANSI_NORMAL "\n", categories[category]);
|
|
||||||
|
|
||||||
Table2 table;
|
|
||||||
for (auto & [name, command] : commands) {
|
|
||||||
auto descr = command->description();
|
|
||||||
if (!descr.empty())
|
|
||||||
table.push_back(std::make_pair(name, descr));
|
|
||||||
}
|
|
||||||
printTable(out, table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
|
bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
{
|
{
|
||||||
if (Args::processFlag(pos, end)) return true;
|
if (Args::processFlag(pos, end)) return true;
|
||||||
|
|
|
@ -20,8 +20,6 @@ public:
|
||||||
wrong. */
|
wrong. */
|
||||||
void parseCmdline(const Strings & cmdline);
|
void parseCmdline(const Strings & cmdline);
|
||||||
|
|
||||||
virtual void printHelp(const string & programName, std::ostream & out);
|
|
||||||
|
|
||||||
/* Return a short one-line description of the command. */
|
/* Return a short one-line description of the command. */
|
||||||
virtual std::string description() { return ""; }
|
virtual std::string description() { return ""; }
|
||||||
|
|
||||||
|
@ -115,8 +113,6 @@ protected:
|
||||||
|
|
||||||
virtual bool processFlag(Strings::iterator & pos, Strings::iterator end);
|
virtual bool processFlag(Strings::iterator & pos, Strings::iterator end);
|
||||||
|
|
||||||
virtual void printFlags(std::ostream & out);
|
|
||||||
|
|
||||||
/* Positional arguments. */
|
/* Positional arguments. */
|
||||||
struct ExpectedArg
|
struct ExpectedArg
|
||||||
{
|
{
|
||||||
|
@ -223,8 +219,6 @@ public:
|
||||||
|
|
||||||
MultiCommand(const Commands & commands);
|
MultiCommand(const Commands & commands);
|
||||||
|
|
||||||
void printHelp(const string & programName, std::ostream & out) override;
|
|
||||||
|
|
||||||
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
|
bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
|
||||||
|
|
||||||
bool processArgs(const Strings & args, bool finish) override;
|
bool processArgs(const Strings & args, bool finish) override;
|
||||||
|
@ -234,14 +228,6 @@ public:
|
||||||
|
|
||||||
Strings argvToStrings(int argc, char * * argv);
|
Strings argvToStrings(int argc, char * * argv);
|
||||||
|
|
||||||
/* Helper function for rendering argument labels. */
|
|
||||||
std::string renderLabels(const Strings & labels);
|
|
||||||
|
|
||||||
/* Helper function for printing 2-column tables. */
|
|
||||||
typedef std::vector<std::pair<std::string, std::string>> Table2;
|
|
||||||
|
|
||||||
void printTable(std::ostream & out, const Table2 & table);
|
|
||||||
|
|
||||||
struct Completion {
|
struct Completion {
|
||||||
std::string completion;
|
std::string completion;
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
|
@ -27,11 +27,6 @@ nix::Commands RegisterCommand::getCommandsFor(const std::vector<std::string> & p
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NixMultiCommand::printHelp(const string & programName, std::ostream & out)
|
|
||||||
{
|
|
||||||
MultiCommand::printHelp(programName, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
nlohmann::json NixMultiCommand::toJSON()
|
nlohmann::json NixMultiCommand::toJSON()
|
||||||
{
|
{
|
||||||
// FIXME: use Command::toJSON() as well.
|
// FIXME: use Command::toJSON() as well.
|
||||||
|
|
|
@ -25,8 +25,6 @@ static constexpr Command::Category catNixInstallation = 102;
|
||||||
|
|
||||||
struct NixMultiCommand : virtual MultiCommand, virtual Command
|
struct NixMultiCommand : virtual MultiCommand, virtual Command
|
||||||
{
|
{
|
||||||
void printHelp(const string & programName, std::ostream & out) override;
|
|
||||||
|
|
||||||
nlohmann::json toJSON() override;
|
nlohmann::json toJSON() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ static bool haveInternet()
|
||||||
std::string programPath;
|
std::string programPath;
|
||||||
char * * savedArgv;
|
char * * savedArgv;
|
||||||
|
|
||||||
|
struct HelpRequested { };
|
||||||
|
|
||||||
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
{
|
{
|
||||||
bool printBuildLogs = false;
|
bool printBuildLogs = false;
|
||||||
|
@ -71,22 +73,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
addFlag({
|
addFlag({
|
||||||
.longName = "help",
|
.longName = "help",
|
||||||
.description = "Show usage information.",
|
.description = "Show usage information.",
|
||||||
.handler = {[&]() { if (!completions) showHelpAndExit(); }},
|
.handler = {[&]() { throw HelpRequested(); }},
|
||||||
});
|
|
||||||
|
|
||||||
addFlag({
|
|
||||||
.longName = "help-config",
|
|
||||||
.description = "Show configuration settings.",
|
|
||||||
.handler = {[&]() {
|
|
||||||
std::cout << "The following configuration settings are available:\n\n";
|
|
||||||
Table2 tbl;
|
|
||||||
std::map<std::string, Config::SettingInfo> settings;
|
|
||||||
globalConfig.getSettings(settings);
|
|
||||||
for (const auto & s : settings)
|
|
||||||
tbl.emplace_back(s.first, s.second.description);
|
|
||||||
printTable(std::cout, tbl);
|
|
||||||
throw Exit();
|
|
||||||
}},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -154,33 +141,6 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printFlags(std::ostream & out) override
|
|
||||||
{
|
|
||||||
Args::printFlags(out);
|
|
||||||
std::cout <<
|
|
||||||
"\n"
|
|
||||||
"In addition, most configuration settings can be overriden using '--" ANSI_ITALIC "name value" ANSI_NORMAL "'.\n"
|
|
||||||
"Boolean settings can be overriden using '--" ANSI_ITALIC "name" ANSI_NORMAL "' or '--no-" ANSI_ITALIC "name" ANSI_NORMAL "'. See 'nix\n"
|
|
||||||
"--help-config' for a list of configuration settings.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void printHelp(const string & programName, std::ostream & out) override
|
|
||||||
{
|
|
||||||
MultiCommand::printHelp(programName, out);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
out << "\nFor full documentation, run 'man " << programName << "' or 'man " << programName << "-" ANSI_ITALIC "COMMAND" ANSI_NORMAL "'.\n";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::cout << "\nNote: this program is " ANSI_RED "EXPERIMENTAL" ANSI_NORMAL " and subject to change.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void showHelpAndExit()
|
|
||||||
{
|
|
||||||
printHelp(programName, std::cout);
|
|
||||||
throw Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string description() override
|
std::string description() override
|
||||||
{
|
{
|
||||||
return "a tool for reproducible and declarative configuration management";
|
return "a tool for reproducible and declarative configuration management";
|
||||||
|
@ -298,6 +258,18 @@ void mainWrapped(int argc, char * * argv)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
args.parseCmdline(argvToStrings(argc, argv));
|
args.parseCmdline(argvToStrings(argc, argv));
|
||||||
|
} catch (HelpRequested &) {
|
||||||
|
std::vector<std::string> subcommand;
|
||||||
|
MultiCommand * command = &args;
|
||||||
|
while (command) {
|
||||||
|
if (command && command->command) {
|
||||||
|
subcommand.push_back(command->command->first);
|
||||||
|
command = dynamic_cast<MultiCommand *>(&*command->command->second);
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
showHelp(subcommand);
|
||||||
|
return;
|
||||||
} catch (UsageError &) {
|
} catch (UsageError &) {
|
||||||
if (!completions) throw;
|
if (!completions) throw;
|
||||||
}
|
}
|
||||||
|
@ -306,7 +278,8 @@ void mainWrapped(int argc, char * * argv)
|
||||||
|
|
||||||
initPlugins();
|
initPlugins();
|
||||||
|
|
||||||
if (!args.command) args.showHelpAndExit();
|
if (!args.command)
|
||||||
|
throw UsageError("no subcommand specified");
|
||||||
|
|
||||||
if (args.command->first != "repl"
|
if (args.command->first != "repl"
|
||||||
&& args.command->first != "doctor"
|
&& args.command->first != "doctor"
|
||||||
|
|
|
@ -28,11 +28,6 @@ struct CmdNar : NixMultiCommand
|
||||||
command->second->prepare();
|
command->second->prepare();
|
||||||
command->second->run();
|
command->second->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void printHelp(const string & programName, std::ostream & out) override
|
|
||||||
{
|
|
||||||
MultiCommand::printHelp(programName, out);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static auto rCmdNar = registerCommand<CmdNar>("nar");
|
static auto rCmdNar = registerCommand<CmdNar>("nar");
|
||||||
|
|
|
@ -21,11 +21,6 @@ struct CmdStore : virtual NixMultiCommand
|
||||||
command->second->prepare();
|
command->second->prepare();
|
||||||
command->second->run();
|
command->second->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void printHelp(const string & programName, std::ostream & out) override
|
|
||||||
{
|
|
||||||
MultiCommand::printHelp(programName, out);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static auto rCmdStore = registerCommand<CmdStore>("store");
|
static auto rCmdStore = registerCommand<CmdStore>("store");
|
||||||
|
|
Loading…
Reference in a new issue