Implement auto-completion support for 'icinga2 console'
authorGunnar Beutner <gunnar@beutner.name>
Wed, 18 Mar 2015 06:17:15 +0000 (07:17 +0100)
committerGunnar Beutner <gunnar@beutner.name>
Wed, 18 Mar 2015 06:17:15 +0000 (07:17 +0100)
refs #8776

lib/cli/consolecommand.cpp
lib/cli/editline.hpp

index 227ab6be5cd7b376438578806313d5c5b750a0e6..cd8389a8199b68759ccfa719beb06d0e19d36827 100644 (file)
@@ -22,6 +22,7 @@
 #include "base/json.hpp"
 #include "base/console.hpp"
 #include "base/application.hpp"
+#include "base/objectlock.hpp"
 #include "base/unixsocket.hpp"
 #include "base/utility.hpp"
 #include "base/networkstream.hpp"
@@ -33,6 +34,8 @@
 using namespace icinga;
 namespace po = boost::program_options;
 
+static ScriptFrame l_ScriptFrame;
+
 REGISTER_CLICOMMAND("console", ConsoleCommand);
 
 String ConsoleCommand::GetDescription(void) const
@@ -58,6 +61,74 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
        ;
 }
 
+#ifdef HAVE_EDITLINE
+static void AddSuggestion(std::vector<String>& matches, const String& word, const String& suggestion)
+{
+       if (suggestion.Find(word) != 0)
+               return;
+
+       matches.push_back(suggestion);
+}
+
+static char *ConsoleCompleteHelper(const char *word, int state)
+{
+       static std::vector<String> matches;
+       String aword = word;
+
+       if (state == 0) {
+               matches.clear();
+
+               AddSuggestion(matches, word, "object");
+               AddSuggestion(matches, word, "template");
+               AddSuggestion(matches, word, "include");
+               AddSuggestion(matches, word, "include_recursive");
+               AddSuggestion(matches, word, "library");
+               AddSuggestion(matches, word, "null");
+               AddSuggestion(matches, word, "true");
+               AddSuggestion(matches, word, "false");
+               AddSuggestion(matches, word, "const");
+               AddSuggestion(matches, word, "var");
+               AddSuggestion(matches, word, "this");
+               AddSuggestion(matches, word, "globals");
+               AddSuggestion(matches, word, "locals");
+               AddSuggestion(matches, word, "use");
+               AddSuggestion(matches, word, "apply");
+               AddSuggestion(matches, word, "to");
+               AddSuggestion(matches, word, "where");
+               AddSuggestion(matches, word, "import");
+               AddSuggestion(matches, word, "assign");
+               AddSuggestion(matches, word, "ignore");
+               AddSuggestion(matches, word, "function");
+               AddSuggestion(matches, word, "return");
+               AddSuggestion(matches, word, "break");
+               AddSuggestion(matches, word, "continue");
+               AddSuggestion(matches, word, "for");
+               AddSuggestion(matches, word, "if");
+               AddSuggestion(matches, word, "else");
+               AddSuggestion(matches, word, "while");
+
+               {
+                       ObjectLock olock(l_ScriptFrame.Locals);
+                       BOOST_FOREACH(const Dictionary::Pair& kv, l_ScriptFrame.Locals) {
+                               AddSuggestion(matches, word, kv.first);
+                       }
+               }
+
+               {
+                       ObjectLock olock(ScriptGlobal::GetGlobals());
+                       BOOST_FOREACH(const Dictionary::Pair& kv, ScriptGlobal::GetGlobals()) {
+                               AddSuggestion(matches, word, kv.first);
+                       }
+               }
+       }
+
+       if (state >= matches.size())
+               return NULL;
+
+       return strdup(matches[state].CStr());
+}
+#endif /* HAVE_EDITLINE */
+
 /**
  * The entry point for the "console" CLI command.
  *
@@ -65,10 +136,13 @@ void ConsoleCommand::InitParameters(boost::program_options::options_description&
  */
 int ConsoleCommand::Run(const po::variables_map& vm, const std::vector<std::string>& ap) const
 {
-       ScriptFrame frame;
        std::map<String, String> lines;
        int next_line = 1;
 
+#ifdef HAVE_EDITLINE
+       rl_completion_entry_function = ConsoleCompleteHelper;
+#endif /* HAVE_EDITLINE */
+
        String addr, session;
 
        if (vm.count("connect")) {
@@ -139,7 +213,7 @@ incomplete:
                                expr = ConfigCompiler::CompileText(fileName, command);
 
                                if (expr) {
-                                       Value result = expr->Evaluate(frame);
+                                       Value result = expr->Evaluate(l_ScriptFrame);
                                        std::cout << ConsoleColorTag(Console_ForegroundCyan);
                                        if (!result.IsObject() || result.IsObjectType<Array>() || result.IsObjectType<Dictionary>())
                                                std::cout << JsonEncode(result);
index 141991fe43e86b6b5f5208f5f3695e8053426e6a..2ae7ab7b57f3707a19a96a73c839904dba51cd2e 100644 (file)
@@ -25,6 +25,10 @@ extern "C" {
 char *readline(const char *prompt);
 int add_history(const char *line);
 
+typedef char *ELFunction(const char *, int);
+
+extern ELFunction *rl_completion_entry_function;
+
 }
 
 #endif /* EDITLINE_H */