diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index f48a8f8f..53bb8fae 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -1834,7 +1834,21 @@ class App { } /// Get a display name for an app - std::string get_display_name() const { return (!name_.empty()) ? name_ : "[Option Group: " + get_group() + "]"; } + std::string get_display_name(bool with_aliases = false) const { + if(name_.empty()) { + return std::string("[Option Group: ") + get_group() + "]"; + } + if(aliases_.empty() || !with_aliases || aliases_.empty()) { + return name_; + } + std::string dispname = name_; + for(const auto &lalias : aliases_) { + dispname.push_back(','); + dispname.push_back(' '); + dispname.append(lalias); + } + return dispname; + } /// Check the name, case insensitive and underscore insensitive if set bool check_name(std::string name_to_check) const { diff --git a/include/CLI/Formatter.hpp b/include/CLI/Formatter.hpp index bf955384..daae9315 100644 --- a/include/CLI/Formatter.hpp +++ b/include/CLI/Formatter.hpp @@ -205,15 +205,18 @@ inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mod inline std::string Formatter::make_subcommand(const App *sub) const { std::stringstream out; - detail::format_help(out, sub->get_name(), sub->get_description(), column_width_); + detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_); return out.str(); } inline std::string Formatter::make_expanded(const App *sub) const { std::stringstream out; - out << sub->get_display_name() << "\n"; + out << sub->get_display_name(true) << "\n"; out << make_description(sub); + if(sub->get_name().empty() && !sub->get_aliases().empty()) { + detail::format_aliases(out, sub->get_aliases(), column_width_ + 2); + } out << make_positionals(sub); out << make_groups(sub, AppFormatMode::Sub); out << make_subcommands(sub, AppFormatMode::Sub); diff --git a/include/CLI/StringTools.hpp b/include/CLI/StringTools.hpp index e1f0df7e..f098a3d8 100644 --- a/include/CLI/StringTools.hpp +++ b/include/CLI/StringTools.hpp @@ -159,7 +159,7 @@ inline std::string trim_copy(const std::string &str, const std::string &filter) return trim(s, filter); } /// Print a two part "help" string -inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, std::size_t wid) { +inline std::ostream &format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) { name = " " + name; out << std::setw(static_cast(wid)) << std::left << name; if(!description.empty()) { @@ -176,6 +176,24 @@ inline std::ostream &format_help(std::ostream &out, std::string name, std::strin return out; } +/// Print subcommand aliases +inline std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { + if(!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for(const auto &alias : aliases) { + if(!front) { + out << ", "; + } else { + front = false; + } + out << alias; + } + out << "\n"; + } + return out; +} + /// Verify the first character of an option template bool valid_first_char(T c) { return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@'; diff --git a/tests/HelpTest.cpp b/tests/HelpTest.cpp index 161b0b9e..a6169dfe 100644 --- a/tests/HelpTest.cpp +++ b/tests/HelpTest.cpp @@ -467,6 +467,36 @@ TEST(THelp, Subcom) { EXPECT_THAT(help, HasSubstr("Usage: ./myprogram sub2")); } +TEST(THelp, Subcom_alias) { + CLI::App app{"My prog"}; + + auto sub1 = app.add_subcommand("sub1", "Subcommand1 description test"); + sub1->alias("sub_alias1"); + sub1->alias("sub_alias2"); + + app.add_subcommand("sub2", "Subcommand2 description test"); + + std::string help = app.help(); + EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] [SUBCOMMAND]")); + EXPECT_THAT(help, HasSubstr("sub_alias1")); + EXPECT_THAT(help, HasSubstr("sub_alias2")); +} + +TEST(THelp, Subcom_alias_group) { + CLI::App app{"My prog"}; + + auto sub1 = app.add_subcommand("", "Subcommand1 description test"); + sub1->alias("sub_alias1"); + sub1->alias("sub_alias2"); + + app.add_subcommand("sub2", "Subcommand2 description test"); + + std::string help = app.help(); + EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] [SUBCOMMAND]")); + EXPECT_THAT(help, HasSubstr("sub_alias1")); + EXPECT_THAT(help, HasSubstr("sub_alias2")); +} + TEST(THelp, MasterName) { CLI::App app{"My prog", "MyRealName"};