[libvirt] [PATCHv2 3/4] Improve virsh autocompletion (global ctl object)

Solly Ross sross at redhat.com
Wed Apr 2 01:34:15 UTC 2014


This patch introduces a way for completers to retrieve the
current vshControl object by using the vshGetCompleterCtl()
macro.  When readline is enabled, the main method stores the
vshControl pointer in a variable that is file-global to virsh.c.
Then, that pointer can be retrieved by the _vshGetCompleterCtl()
function, which is mapped to by the macro.  If readline is not
enabled, the macro simply maps to NULL.
---
 tests/virshtest.c | 13 +++++++++++++
 tools/virsh.c     | 52 +++++++++++++++++++++++++++++++++++++++++++++++-----
 tools/virsh.h     |  8 ++++++++
 3 files changed, 68 insertions(+), 5 deletions(-)

diff --git a/tests/virshtest.c b/tests/virshtest.c
index 0c698fa..d068e5e 100644
--- a/tests/virshtest.c
+++ b/tests/virshtest.c
@@ -333,6 +333,15 @@ static int testCompletionInArgvMode(const void *data ATTRIBUTE_UNUSED)
     return testCompareOutputLit(exp, NULL, argv);
 }
 
+static int testCompletionRequiringCtl(const void *data ATTRIBUTE_UNUSED)
+{
+    const char *const argv[] = { VIRSH_CUSTOM, "complete",
+                                 "fake-command --string3 ", NULL };
+    const char *exp = ("test:///home/directxman12/dev/libvirt"
+                       "/tests/../examples/xml/test/testnode.xml\n\n");
+    return testCompareOutputLit(exp, NULL, argv);
+}
+
 # endif /* WITH_READLINE */
 
 
@@ -575,6 +584,10 @@ mymain(void)
                     testCompletionInArgvMode, NULL) != 0)
         ret = -1;
 
+    if (virtTestRun("virsh completion (completer requiring control object)",
+                    testCompletionRequiringCtl, NULL) != 0)
+        ret = -1;
+
 # endif /* WITH_READLINE */
 
     /* It's a bit awkward listing result before argument, but that's a
diff --git a/tools/virsh.c b/tools/virsh.c
index 808a125..71076dc 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -96,6 +96,16 @@ static char *progname;
 
 static const vshCmdGrp cmdGroups[];
 
+#if WITH_READLINE
+static vshControl *completer_ctl;
+
+vshControl *
+_vshGetCompleterCtl(void)
+{
+    return completer_ctl;
+}
+#endif
+
 /* Bypass header poison */
 #undef strdup
 
@@ -1033,6 +1043,21 @@ VSH_STRING_COMPLETER(NULL, FakeCommandStr2, "value a", "value b");
 VSH_STRING_COMPLETER(NULL, FakeCommandData1, "ab", "cd");
 VSH_STRING_COMPLETER(NULL, FakeCommandData2, "i e f", "i g h");
 
+static char **
+vshCompleteFakeCommandStr3(unsigned int flags)
+{
+    virCheckFlags(0, NULL);
+
+    vshControl *ctl = vshGetCompleterCtl();
+    char *uri = virConnectGetURI(ctl->conn);
+    char **res = vshCalloc(NULL, 2, sizeof(char*));
+
+    res[0] = uri;
+    res[1] = NULL;
+
+    return res;
+}
+
 static const vshCmdOptDef opts_fake_command[] = {
     {.name = "abool",
      .type = VSH_OT_BOOL,
@@ -1060,6 +1085,7 @@ static const vshCmdOptDef opts_fake_command[] = {
     },
     {.name = "string3",
      .type = VSH_OT_STRING,
+     .completer = vshCompleteFakeCommandStr3,
      .help = N_("another string")
     },
     {.name = "file",
@@ -3112,7 +3138,7 @@ vshReadlineCommandGenerator(const char *text, int state)
 }
 
 static char *
-vshDelegateToCustomCompleter(const vshCmdOptDef *opt,
+vshDelegateToCustomCompleter(const vshCmdOptDef *opt, bool reconnect,
                              const char *text, int state)
 {
     static int list_index;
@@ -3125,6 +3151,10 @@ vshDelegateToCustomCompleter(const vshCmdOptDef *opt,
             return NULL;
 
         if (opt->completer) {
+            vshControl *ctl = completer_ctl;
+            if ((!ctl->conn || disconnected) && reconnect)
+                vshReconnect(ctl);
+
             list_index = 0;
             completions = opt->completer(opt->completer_flags);
         }
@@ -3276,10 +3306,13 @@ vshReadlineOptionsGenerator(const char *text, int state)
 
     if (waiting_for_flag_arg) {
         char* res;
+        bool may_connect = !(cmd->flags & VSH_CMD_FLAG_NOCONNECT);
         if (continue_from_error)
-            res = vshDelegateToCustomCompleter(curr_opt, last_tok, substate);
+            res = vshDelegateToCustomCompleter(curr_opt, may_connect,
+                                               last_tok, substate);
         else
-            res = vshDelegateToCustomCompleter(curr_opt, text, substate);
+            res = vshDelegateToCustomCompleter(curr_opt, may_connect,
+                                               text, substate);
 
         substate++;
         /* if we're in a flag's argument, we don't
@@ -3343,10 +3376,14 @@ vshReadlineOptionsGenerator(const char *text, int state)
 
             /* we don't need to ignore args without custom completers,
              * since vshDelegateToCustomCompleter will do this for us */
+            bool may_connect = !(cmd->flags & VSH_CMD_FLAG_NOCONNECT);
             if (continue_from_error)
-                res = vshDelegateToCustomCompleter(opt, last_tok, substate);
+                res = vshDelegateToCustomCompleter(opt, may_connect,
+                                                   last_tok, substate);
             else
-                res = vshDelegateToCustomCompleter(opt, text, substate);
+                res = vshDelegateToCustomCompleter(opt, may_connect,
+                                                   text, substate);
+
             substate++;
             if (res) {
                 if (strchr(res, ' ')) {
@@ -4134,9 +4171,14 @@ int
 main(int argc, char **argv)
 {
     vshControl _ctl, *ctl = &_ctl;
+
     const char *defaultConn;
     bool ret = true;
 
+#if WITH_READLINE
+    completer_ctl = ctl;
+#endif
+
     memset(ctl, 0, sizeof(vshControl));
     ctl->imode = true;          /* default is interactive mode */
     ctl->log_fd = -1;           /* Initialize log file descriptor */
diff --git a/tools/virsh.h b/tools/virsh.h
index 227e4e8..38f3663 100644
--- a/tools/virsh.h
+++ b/tools/virsh.h
@@ -180,6 +180,14 @@ struct _vshCmdOptDef {
  * readline file completer */
 # define VSH_COMPLETE_AS_FILE (1 << 8)
 
+vshControl * _vshGetCompleterCtl(void);
+
+# if WITH_READLINE
+#  define vshGetCompleterCtl() _vshGetCompleterCtl()
+# else
+#  define vshGetCompleterCtl() NULL
+# endif
+
 /*
  * vshCmdOpt - command options
  *
-- 
1.8.3.2




More information about the libvir-list mailing list