#include #include "argparse_formatter.hpp" #include "argparse_util.hpp" #include "argparse.hpp" namespace argparse { constexpr size_t OPTION_HELP_SLACK = 2; std::string INDENT = " "; std::string USAGE_PREFIX = "usage: "; std::string long_option_str(const Argument& argument); std::string short_option_str(const Argument& argument); std::string determine_metavar(const Argument& argument); /* * DefaultFormatter */ DefaultFormatter::DefaultFormatter(size_t option_name_width, size_t total_width) : option_name_width_(option_name_width) , total_width_(total_width) {} void DefaultFormatter::set_parser(ArgumentParser* parser) { parser_ = parser; } std::string DefaultFormatter::format_usage() const { if (!parser_) throw ArgParseError("parser not initialized in help formatter"); std::stringstream ss; ss << USAGE_PREFIX << parser_->prog(); int num_unshown_options = 0; for (const auto& group : parser_->argument_groups()) { auto args = group.arguments(); for(const auto& arg : args) { if(arg->show_in() != ShowIn::USAGE_AND_HELP) { num_unshown_options++; continue; } ss << " "; if (!arg->required()) { ss << "["; } auto short_opt = short_option_str(*arg); if (!short_opt.empty()) { ss << short_opt; } else { ss << long_option_str(*arg); } if (!arg->required()) { ss << "]"; } } } if (num_unshown_options > 0) { ss << " [OTHER_OPTIONS ...]"; } size_t prefix_len = USAGE_PREFIX.size(); std::stringstream wrapped_ss; bool first = true; for(const auto& line : wrap_width(ss.str(), total_width_ - prefix_len, {" [", " -"})) { if(!first) { //pass wrapped_ss << std::string(prefix_len, ' '); } wrapped_ss << line; first = false; } wrapped_ss << "\n"; return wrapped_ss.str(); } std::string DefaultFormatter::format_description() const { if (!parser_) throw ArgParseError("parser not initialized in help formatter"); std::stringstream ss; ss << "\n"; for(auto& line : wrap_width(parser_->description(), total_width_)) { ss << line; } ss << "\n"; return ss.str(); } std::string DefaultFormatter::format_arguments() const { if (!parser_) throw ArgParseError("parser not initialized in help formatter"); std::stringstream ss; for (const auto& group : parser_->argument_groups()) { auto args = group.arguments(); if (args.size() > 0) { ss << "\n"; ss << group.name() << ":" << "\n"; for (const auto& arg : args) { std::stringstream arg_ss; arg_ss << std::boolalpha; auto long_opt_descr = long_option_str(*arg); auto short_opt_descr = short_option_str(*arg); //name/option arg_ss << INDENT; if (!short_opt_descr.empty()) { arg_ss << short_opt_descr; } if (!long_opt_descr.empty()) { if (!short_opt_descr.empty()) { arg_ss << ", "; } arg_ss << long_opt_descr; } size_t pos = arg_ss.str().size(); if (pos + OPTION_HELP_SLACK > option_name_width_) { //If the option name is too long, wrap the help //around to a new line arg_ss << "\n"; pos = 0; } //Argument help auto help_lines = wrap_width(arg->help(), total_width_ - option_name_width_); for (auto& line : help_lines) { //Pad out the help assert(pos <= option_name_width_); arg_ss << std::string(option_name_width_ - pos, ' '); //Print a wrapped line arg_ss << line; pos = 0; } //Default if (!arg->default_value().empty()) { if(!arg->help().empty()) { arg_ss << " "; } arg_ss << "(Default: " << arg->default_value() << ")"; } arg_ss << "\n"; ss << arg_ss.str(); } if (!group.epilog().empty()) { ss << "\n"; auto epilog_lines = wrap_width(group.epilog(), total_width_ - INDENT.size()); for (auto& line : epilog_lines) { ss << INDENT << line; } ss << "\n"; } } } return ss.str(); } std::string DefaultFormatter::format_epilog() const { if (!parser_) throw ArgParseError("parser not initialized in help formatter"); std::stringstream ss; ss << "\n"; for(auto& line : wrap_width(parser_->epilog(), total_width_)) { ss << line; } ss << "\n"; return ss.str(); } std::string DefaultFormatter::format_version() const { if (!parser_) throw ArgParseError("parser not initialized in help formatter"); return parser_->version() + "\n"; } /* * Utilities */ std::string long_option_str(const Argument& argument) { auto long_opt = argument.long_option(); if(argument.nargs() != '0' && !argument.positional()) { long_opt += + " " + determine_metavar(argument); } return long_opt; } std::string short_option_str(const Argument& argument) { auto short_opt = argument.short_option(); if(!short_opt.empty()) { if(argument.nargs() != '0' && !argument.positional()) { short_opt += " " + determine_metavar(argument); } return short_opt; } else { return ""; } } std::string determine_metavar(const Argument& arg) { std::string base_metavar = arg.metavar(); if (!arg.choices().empty()) { //We allow choices to override the default metavar std::stringstream choices_ss; choices_ss << "{"; bool first = true; for(auto choice : arg.choices()) { if (!first) { choices_ss << ", "; } choices_ss << choice; first = false; } choices_ss << "}"; base_metavar = choices_ss.str(); } std::string metavar; if (arg.nargs() == '0' || arg.positional()) { //empty metavar = ""; } else if (arg.nargs() == '1') { metavar = base_metavar; } else if (arg.nargs() == '?') { metavar = "[" + base_metavar + "]"; } else if (arg.nargs() == '+') { metavar = base_metavar + " [" + base_metavar + " ...]"; } else if (arg.nargs() == '*') { metavar = "[" + base_metavar + " [" + base_metavar + " ...]]"; } else { assert(false); } return metavar; } } //namespace