# # # add_dir "tests/automate_commands_are_not_completed" # # add_file "tests/automate_commands_are_not_completed/__driver__.lua" # content [ed738d929615e1e6a14ee29c909bd3a67e72c158] # # patch "cmd.hh" # from [a0b681abc8d9e4c1bacba9872c74ca3d0aefb6fd] # to [a35952c265bbeaf6590cdcd8b32369adbf50fefc] # # patch "cmd_automate.cc" # from [a8202aed4b5200453cfd3a4fc14845ccef0c183d] # to [8cc80cf3110f3adbc4f3516f24604f4e1340f01c] # # patch "commands.cc" # from [09c0a2860ca0dcf1219b0df0e181c157ec6038af] # to [0ff6dc326fda4fd098f933d98da5e89f1eb9a1ef] # ============================================================ --- tests/automate_commands_are_not_completed/__driver__.lua ed738d929615e1e6a14ee29c909bd3a67e72c158 +++ tests/automate_commands_are_not_completed/__driver__.lua ed738d929615e1e6a14ee29c909bd3a67e72c158 @@ -0,0 +1,14 @@ + +mtn_setup() + +-- No completion +check(mtn("automate", "leaves"), 0, false, false) + +-- Complete subcommand +check(mtn("automate", "lea"), 1, false, false) + +-- Complete "automate" +check(mtn("automat", "leaves"), 1, false, false) + +-- Alias for people who like using automate manually +check(mtn("auto", "leaves"), 0, false, false) ============================================================ --- cmd.hh a0b681abc8d9e4c1bacba9872c74ca3d0aefb6fd +++ cmd.hh a35952c265bbeaf6590cdcd8b32369adbf50fefc @@ -42,12 +42,14 @@ namespace commands bool m_use_workspace_options; options::options_type m_opts; children_set m_children; + bool m_allow_completion; std::map< command_id, command * > - find_completions(utf8 const & prefix, command_id const & completed) - const; + find_completions(utf8 const & prefix, command_id const & completed, + bool completion_ok = true) const; command * find_child_by_name(utf8 const & name) const; + bool allow_completion() const; public: command(std::string const & primary_name, std::string const & other_names, @@ -57,7 +59,8 @@ namespace commands std::string const & abstract, std::string const & desc, bool use_workspace_options, - options::options_type const & opts); + options::options_type const & opts, + bool allow_completion); virtual ~command(void); @@ -80,13 +83,14 @@ namespace commands virtual void exec(app_state & app, command_id const & execid, - args_vector const & args) = 0; + args_vector const & args) const = 0; bool has_name(utf8 const & name) const; - command * find_command(command_id const & id); + command const * find_command(command_id const & id) const; std::set< command_id > complete_command(command_id const & id, - command_id completed = command_id()) const; + command_id completed = command_id(), + bool completion_ok = true) const; }; class automate : public command @@ -112,7 +116,7 @@ namespace commands void exec(app_state & app, command_id const & execid, - args_vector const & args); + args_vector const & args) const; }; }; @@ -177,17 +181,17 @@ namespace commands { public: \ cmd_ ## C() : command(name, aliases, parent, hidden, params, \ abstract, desc, true, \ - options::options_type() | opts) \ + options::options_type() | opts, true) \ {} \ virtual void exec(app_state & app, \ command_id const & execid, \ - args_vector const & args); \ + args_vector const & args) const; \ }; \ cmd_ ## C C ## _cmd; \ } \ void commands::cmd_ ## C::exec(app_state & app, \ command_id const & execid, \ - args_vector const & args) + args_vector const & args) const #define CMD(C, name, aliases, parent, params, abstract, desc, opts) \ _CMD2(C, name, aliases, parent, false, params, abstract, desc, opts) @@ -195,28 +199,34 @@ void commands::cmd_ ## C::exec(app_state #define CMD_HIDDEN(C, name, aliases, parent, params, abstract, desc, opts) \ _CMD2(C, name, aliases, parent, true, params, abstract, desc, opts) -#define CMD_GROUP(C, name, aliases, parent, abstract, desc) \ -namespace commands { \ +#define _CMD_GROUP2(C, name, aliases, parent, abstract, desc, cmpl) \ + namespace commands { \ class cmd_ ## C : public command \ { \ public: \ cmd_ ## C() : command(name, aliases, parent, false, "", abstract,\ desc, true, \ - options::options_type()) \ + options::options_type(), cmpl) \ {} \ virtual void exec(app_state & app, \ command_id const & execid, \ - args_vector const & args); \ + args_vector const & args) const; \ }; \ cmd_ ## C C ## _cmd; \ } \ void commands::cmd_ ## C::exec(app_state & app, \ command_id const & execid, \ - args_vector const & args) \ + args_vector const & args) const \ { \ I(false); \ } +#define CMD_GROUP(C, name, aliases, parent, abstract, desc) \ + _CMD_GROUP2(C, name, aliases, parent, abstract, desc, true) + +#define CMD_GROUP_NO_COMPLETE(C, name, aliases, parent, abstract, desc) \ + _CMD_GROUP2(C, name, aliases, parent, abstract, desc, false) + // Use this for commands that should specifically _not_ look for an // _MTN dir and load options from it. @@ -228,17 +238,17 @@ namespace commands { public: \ cmd_ ## C() : command(name, aliases, parent, false, params, \ abstract, desc, false, \ - options::options_type() | opts) \ + options::options_type() | opts, true) \ {} \ virtual void exec(app_state & app, \ command_id const & execid, \ - args_vector const & args); \ + args_vector const & args) const; \ }; \ cmd_ ## C C ## _cmd; \ } \ void commands::cmd_ ## C::exec(app_state & app, \ command_id const & execid, \ - args_vector const & args) \ + args_vector const & args) const // TODO: 'abstract' and 'desc' should be refactored so that the // command definition allows the description of input/output format, ============================================================ --- cmd_automate.cc a8202aed4b5200453cfd3a4fc14845ccef0c183d +++ cmd_automate.cc 8cc80cf3110f3adbc4f3516f24604f4e1340f01c @@ -22,7 +22,7 @@ using std::vector; using std::string; using std::vector; -CMD_GROUP(automate, "automate", "", CMD_REF(automation), +CMD_GROUP(automate, "automate", "auto", CMD_REF(automation), N_("Interface for scripted execution"), ""); @@ -33,14 +33,14 @@ namespace commands { string const & desc, options::options_type const & opts) : command(name, "", CMD_REF(automate), false, params, abstract, - desc, true, opts) + desc, true, opts, false) { } void automate::exec(app_state & app, command_id const & execid, - args_vector const & args) + args_vector const & args) const { make_io_binary(); exec_from_automate(args, execid, app, std::cout); @@ -366,9 +366,9 @@ CMD_AUTOMATE(stdio, "", for (command_id::size_type i = 0; i < id.size(); i++) args.erase(args.begin()); - command * cmd = CMD_REF(automate)->find_command(id); + command const * cmd = CMD_REF(automate)->find_command(id); I(cmd != NULL); - automate * acmd = reinterpret_cast< automate * >(cmd); + automate const * acmd = reinterpret_cast< automate const * >(cmd); opts = options::opts::globals() | acmd->opts(); opts.instantiate(&app.opts).from_key_value_pairs(params); ============================================================ --- commands.cc 09c0a2860ca0dcf1219b0df0e181c157ec6038af +++ commands.cc 0ff6dc326fda4fd098f933d98da5e89f1eb9a1ef @@ -53,9 +53,9 @@ CMD_GROUP(__root__, "__root__", "", NULL // maybe this should be revised, because exposing the top level category // (being optional, of course), may not be a bad idea. // -CMD_GROUP(automation, "automation", "", CMD_REF(__root__), - N_("Commands that aid in scripted execution"), - ""); +CMD_GROUP_NO_COMPLETE(automation, "automation", "", CMD_REF(__root__), + N_("Commands that aid in scripted execution"), + ""); CMD_GROUP(database, "database", "", CMD_REF(__root__), N_("Commands that manipulate the database"), ""); @@ -148,7 +148,8 @@ namespace commands { std::string const & abstract, std::string const & desc, bool use_workspace_options, - options::options_type const & opts) + options::options_type const & opts, + bool _allow_completion) : m_primary_name(utf8(primary_name)), m_parent(parent), m_hidden(hidden), @@ -156,7 +157,8 @@ namespace commands { m_abstract(utf8(abstract)), m_desc(utf8(desc)), m_use_workspace_options(use_workspace_options), - m_opts(opts) + m_opts(opts), + m_allow_completion(_allow_completion) { // A warning about the parent pointer: commands are defined as global // variables, so they are initialized during program startup. As they @@ -181,6 +183,13 @@ namespace commands { { } + bool + command::allow_completion() const + { + return m_allow_completion && + (m_parent?m_parent->allow_completion():true); + } + command_id command::ident(void) const { @@ -285,10 +294,10 @@ namespace commands { return names().find(name) != names().end(); } - command * - command::find_command(command_id const & id) + command const * + command::find_command(command_id const & id) const { - command * cmd; + command const * cmd; if (id.empty()) cmd = this; @@ -311,7 +320,8 @@ namespace commands { } map< command_id, command * > - command::find_completions(utf8 const & prefix, command_id const & completed) + command::find_completions(utf8 const & prefix, command_id const & completed, + bool completion_ok) const { map< command_id, command * > matches; @@ -331,7 +341,8 @@ namespace commands { if (prefix == *iter2) matches[caux] = child; else if (!child->hidden() && - prefix().length() < (*iter2)().length()) + prefix().length() < (*iter2)().length() && + allow_completion() && completion_ok) { string temp((*iter2)(), 0, prefix().length()); utf8 p(temp); @@ -346,7 +357,8 @@ namespace commands { set< command_id > command::complete_command(command_id const & id, - command_id completed) const + command_id completed, + bool completion_ok) const { I(this != CMD_REF(__root__) || !id.empty()); I(!id.empty()); @@ -356,7 +368,10 @@ namespace commands { utf8 component = *(id.begin()); command_id remaining(id.begin() + 1, id.end()); - map< command_id, command * > m2 = find_completions(component, completed); + map< command_id, command * > + m2 = find_completions(component, + completed, + allow_completion() && completion_ok); for (map< command_id, command * >::const_iterator iter = m2.begin(); iter != m2.end(); iter++) { @@ -450,6 +465,7 @@ namespace commands iter != matches.end() && tmp.empty(); iter++) { command_id const & id = *iter; + I(id.size() >= 2); if (id[id.size() - 1]() == args[id.size() - 2]()) tmp = id; } @@ -486,10 +502,10 @@ namespace commands return id; } - static command * + static command const * find_command(command_id const & ident) { - command * cmd = CMD_REF(__root__)->find_command(ident); + command const * cmd = CMD_REF(__root__)->find_command(ident); // This function is only used internally with an identifier returned // by complete_command. Therefore, it must always exist. @@ -621,7 +637,7 @@ namespace commands void explain_usage(command_id const & ident, ostream & out) { - command * cmd = find_command(ident); + command const * cmd = find_command(ident); if (ident.empty()) { @@ -646,7 +662,7 @@ namespace commands void process(app_state & app, command_id const & ident, args_vector const & args) { - command * cmd = CMD_REF(__root__)->find_command(ident); + command const * cmd = CMD_REF(__root__)->find_command(ident); string visibleid = join_words(vector< utf8 >(ident.begin() + 1, ident.end()))(); @@ -673,7 +689,7 @@ namespace commands options::options_type command_options(command_id const & ident) { - command * cmd = find_command(ident); + command const * cmd = find_command(ident); return cmd->opts(); } } @@ -1108,63 +1124,63 @@ UNIT_TEST(commands, command_find_command // Non-existent single-word identifier. { command_id id = make_command_id("foo"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == NULL); } // Non-existent multi-word identifier. { command_id id = make_command_id("foo bar"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == NULL); } // Single-word identifier that could be completed. { command_id id = make_command_id("test"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == NULL); } // Single-word identifier. { command_id id = make_command_id("test1"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == CMD_REF(test1)); } // Hidden single-word identifier. { command_id id = make_command_id("test3"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == CMD_REF(test3)); } // Multi-word identifier that could be completed. { command_id id = make_command_id("testg testg"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == NULL); } // Multi-word identifier. { command_id id = make_command_id("testg testg1"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == CMD_REF(testg1)); } // Hidden multi-word identifier. { command_id id = make_command_id("testg testg3"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == CMD_REF(testg3)); } // Multi-word identifier with extra words. { command_id id = make_command_id("testg testg1 foo"); - command * cmd = CMD_REF(top)->find_command(id); + command const * cmd = CMD_REF(top)->find_command(id); UNIT_TEST_CHECK(cmd == NULL); } }