# # # patch "cmd.hh" # from [158e7df2f5aaded06648bb830cd7d82acf0a60f0] # to [6dd61b122a71bc237aff5a33149542e3aad8a0f0] # # patch "commands.cc" # from [096e7d94296b4fbd9b320bfd4922a58b843748b0] # to [08eb1c1b105e755cb376db178167be48a53cb07a] # ============================================================ --- cmd.hh 158e7df2f5aaded06648bb830cd7d82acf0a60f0 +++ cmd.hh 6dd61b122a71bc237aff5a33149542e3aad8a0f0 @@ -10,6 +10,10 @@ // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. +#include +#include +#include + #include "commands.hh" #include "options.hh" #include "sanity.hh" @@ -47,6 +51,12 @@ namespace commands virtual void exec(app_state & app, std::vector const & args) = 0; }; + + // This type and global map maintain a relation between each command name + // and all of its aliases. If the command has no aliases, it is not + // present here. + typedef std::map< std::string, std::set > aliases_map; + extern aliases_map aliases; }; inline std::vector @@ -157,7 +167,20 @@ namespace commands { realcommand##_cmd.abstract_, \ realcommand##_cmd.desc_, true, \ realcommand##_cmd.opts) \ - {} \ + { \ + aliases_map::iterator i = aliases.find(#realcommand); \ + if (i == aliases.end()) \ + { \ + std::set as; \ + as.insert(#C); \ + aliases.insert(aliases_map::value_type(#realcommand, as)); \ + } \ + else \ + { \ + std::set & as = (*i).second; \ + as.insert(#C); \ + } \ + } \ virtual std::string desc(); \ virtual void exec(app_state & app, \ std::vector const & args); \ ============================================================ --- commands.cc 096e7d94296b4fbd9b320bfd4922a58b843748b0 +++ commands.cc 08eb1c1b105e755cb376db178167be48a53cb07a @@ -50,6 +50,8 @@ namespace commands namespace commands { + aliases_map aliases; + const char * safe_gettext(const char * msgid) { if (strlen(msgid) == 0) @@ -149,7 +151,22 @@ namespace commands && (string(_(self.name.c_str())) < (string(_(other.name.c_str())))))); } + static bool is_alias(string const & cmd) + { + bool ia = false; + if (aliases.find(cmd) == aliases.end()) + for (aliases_map::const_iterator iter = aliases.begin(); + iter != aliases.end() && !ia; iter++) + { + set const & as = (*iter).second; + if (as.find(cmd) != as.end()) + ia = true; + } + + return ia; + } + string complete_command(string const & cmd) { if (cmd.length() == 0 || (*cmds).find(cmd) != (*cmds).end()) return cmd; @@ -234,6 +251,36 @@ namespace commands return cmdgroup; } + // Generates a string of the form (a1, ..., aN), preceded by a space + // for simplicity reasons later on, where a1 through aN are the aliases + // for the command cmd. Returns the empty string if no aliases are + // defined for that command. + static string format_aliases(string const & cmd) + { + string text; + + aliases_map::const_iterator iter = aliases.find(cmd); + if (iter != aliases.end()) + { + text = " ("; + + set const & as = (*iter).second; + set::const_iterator asiter = as.begin(); + for (;;) + { + text += *asiter; + asiter++; + if (asiter == as.end()) + break; + text += ", "; + } + + text += ")"; + } + + return text; + } + // Prints the abstract description of the given command or command group // properly indented. The tag starts at column two. The description has // to start, at the very least, two spaces after the tag's end position; @@ -312,11 +359,14 @@ namespace commands for (map::const_iterator i = (*cmds).begin(); i != (*cmds).end(); ++i) { - if (i->second->cmdgroup == cmdgroup) + const string & name = i->second->name; + + if (i->second->cmdgroup == cmdgroup && !is_alias(name)) { sorted.push_back(i->second); - size_t len = display_width(utf8(i->second->name + " ")); + string tag = name + format_aliases(name); + size_t len = display_width(utf8(tag + " ")); if (colabstract < len) colabstract = len; } @@ -330,7 +380,8 @@ namespace commands string const & name = idx(sorted, i)->name; string const & abstract = idx(sorted, i)->abstract(); - describe(name, abstract, colabstract, out); + string tag = name + format_aliases(name); + describe(tag, abstract, colabstract, out); } } @@ -687,6 +738,55 @@ process_commit_message_args(bool & given given = false; } +#ifdef BUILD_UNIT_TESTS +#include "unit_tests.hh" + +CMD(__test1, hidden_group(), "", "", "", options::opts::none) {} + +CMD(__test2, hidden_group(), "", "", "", options::opts::none) {} +ALIAS(__test2_alias1, __test2) + +CMD(__test3, hidden_group(), "", "", "", options::opts::none) {} +ALIAS(__test3_alias1, __test3) +ALIAS(__test3_alias2, __test3) + +UNIT_TEST(commands, is_alias) +{ + using namespace commands; + + // Non-existent command. + BOOST_CHECK(!is_alias("__test0")); + + // Non-alias commands. + BOOST_CHECK(!is_alias("__test1")); + BOOST_CHECK(!is_alias("__test2")); + BOOST_CHECK(!is_alias("__test3")); + + // Alias commands. + BOOST_CHECK(is_alias("__test2_alias1")); + BOOST_CHECK(is_alias("__test3_alias1")); + BOOST_CHECK(is_alias("__test3_alias2")); +} + +UNIT_TEST(commands, format_aliases) +{ + using namespace commands; + + // Non-existent command. + BOOST_CHECK(format_aliases("__test0").empty()); + + // Commands with aliases. + BOOST_CHECK(format_aliases("__test1").empty()); + BOOST_CHECK(format_aliases("__test2") == " (__test2_alias1)"); + BOOST_CHECK(format_aliases("__test3") == " (__test3_alias1, __test3_alias2)"); + + // Alias commands; cannot get their aliases. + BOOST_CHECK(format_aliases("__test2_alias1").empty()); + BOOST_CHECK(format_aliases("__test3_alias1").empty()); + BOOST_CHECK(format_aliases("__test3_alias2").empty()); +} +#endif // BUILD_UNIT_TESTS + // Local Variables: // mode: C++ // fill-column: 76