Make '--help' do the same as 'help' (i.e. show a manpage)

This commit is contained in:
Eelco Dolstra 2021-01-25 14:38:15 +01:00
parent 488a826842
commit b159d23800
7 changed files with 17 additions and 164 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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.

View file

@ -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;
}; };

View file

@ -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"

View file

@ -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");

View file

@ -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");