Fixes to filename shell completions
authorTeemu Murtola <teemu.murtola@gmail.com>
Tue, 28 Jan 2014 20:37:21 +0000 (22:37 +0200)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Wed, 29 Jan 2014 21:22:36 +0000 (22:22 +0100)
- When completing a file name, don't add a space after a directory name
  has been completed.
- Don't exclude directory names starting with . from the completions.
  This also excludes ../foobar/.
- Use a more reasonable pattern to match the file names: expect exactly
  one of the acceptable extensions, and at most one .gz/.Z extension.
- Complete directory names for mdrun -multidir.

Issues that remain:
- Completions for paths that contain spaces doesn't really work.
  The only difference to earlier behavior is that now, completing
  something that starts with a " gets an appended space before the
  closing ".
- When completing to subdirectories, the list of possible completions
  shows the subdirectory for each alternative.  This doesn't happen with
  standard bash completion.  Not sure whether this is feasible to fix.

Part of #1410 and #1159.

Change-Id: I1aff3ab89419e0ed4b53bc998c9a50b9a1879e9c

src/gromacs/commandline/shellcompletions.cpp
src/programs/CMakeLists.txt

index cf3cd9fb1d0bb453de0e9e1a589bec45c4ded398..de4cb802badc8769e606dc900fdfe490517f534a 100644 (file)
@@ -60,12 +60,18 @@ static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[])
 {
     for (int i = 0; i < nf; i++)
     {
-        fprintf(fp, "%s) COMPREPLY=( $(compgen -X '!*.", tfn[i].opt);
         const int ftp          = tfn[i].ftp;
+        if (ftp == efRND)
+        {
+            fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -d $c) );;\n",
+                    tfn[i].opt);
+            continue;
+        }
+        fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -X '!*.", tfn[i].opt);
         const int genericCount = ftp2generic_count(ftp);
         if (genericCount > 0)
         {
-            fprintf(fp, "+(");
+            fprintf(fp, "@(");
             const int *const genericTypes = ftp2generic_list(ftp);
             for (int j = 0; j < genericCount; j++)
             {
@@ -81,7 +87,7 @@ static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[])
         {
             fprintf(fp, "%s", ftp2ext(ftp));
         }
-        fprintf(fp, "*(");
+        fprintf(fp, "?(");
         for (int j = 0; j < NZEXT; j++)
         {
             if (j > 0)
@@ -90,7 +96,7 @@ static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[])
             }
             fprintf(fp, "%s", z_ext[j]);
         }
-        fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
+        fprintf(fp, ")' -f $c ; compgen -S '/' -d $c ));;\n");
     }
 }
 
@@ -98,21 +104,24 @@ static void pr_opts(FILE *fp,
                     int nfile,  t_filenm *fnm,
                     int npargs, t_pargs pa[])
 {
-    fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen  -W '");
+    fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -S ' '  -W $'");
+    const char *sep = "";
     for (int i = 0; i < nfile; i++)
     {
-        fprintf(fp, " -%s", fnm[i].opt+1);
+        fprintf(fp, "%s-%s", sep, fnm[i].opt+1);
+        sep = "\\n";
     }
     for (int i = 0; i < npargs; i++)
     {
         if (pa[i].type == etBOOL && *(pa[i].u.b))
         {
-            fprintf(fp, " -no%s", pa[i].option+1);
+            fprintf(fp, "%s-no%s", sep, pa[i].option + 1);
         }
         else
         {
-            fprintf(fp, " -%s", pa[i].option+1);
+            fprintf(fp, "%s-%s", sep, pa[i].option + 1);
         }
+        sep = "\\n";
     }
     fprintf(fp, "' -- $c)); return 0; fi\n");
 }
@@ -123,12 +132,12 @@ static void pr_enums(FILE *fp, int npargs, t_pargs pa[])
     {
         if (pa[i].type == etENUM)
         {
-            fprintf(fp, "%s) COMPREPLY=( $(compgen -'", pa[i].option);
+            fprintf(fp, "%s) COMPREPLY=( $(compgen -S ' ' -W $'", pa[i].option);
             for (int j = 1; pa[i].u.c[j]; j++)
             {
-                fprintf(fp, " %s", pa[i].u.c[j]);
+                fprintf(fp, "%s%s", (j == 1 ? "" : "\\n"), pa[i].u.c[j]);
             }
-            fprintf(fp, " ' -- $c ));;\n");
+            fprintf(fp, "' -- $c ));;\n");
         }
     }
 }
@@ -143,6 +152,7 @@ static void write_bashcompl(FILE *out,
      */
     fprintf(out, "%s() {\n", funcName);
     fprintf(out, "local p c\n");
+    fprintf(out, "local IFS=$'\\n'\n");
     fprintf(out, "COMPREPLY=() c=${COMP_WORDS[COMP_CWORD]} p=${COMP_WORDS[COMP_CWORD-1]}\n");
     pr_opts(out, nfile, fnm, npargs, pa);
     fprintf(out, "case \"$p\" in\n");
@@ -235,6 +245,7 @@ void ShellCompletionWriter::writeWrapperCompletions(
 {
     impl_->file_->writeLine("_" + impl_->binaryName_ + "_compl() {");
     impl_->file_->writeLine("local i c m");
+    impl_->file_->writeLine("local IFS=$'\\n'\n");
     impl_->file_->writeLine("COMPREPLY=()");
     impl_->file_->writeLine("unset COMP_WORDS[0]");
     impl_->file_->writeLine("for ((i=1;i<COMP_CWORD;++i)) ; do");
@@ -244,14 +255,14 @@ void ShellCompletionWriter::writeWrapperCompletions(
     impl_->file_->writeLine("if (( i == COMP_CWORD )); then");
     impl_->file_->writeLine("c=${COMP_WORDS[COMP_CWORD]}");
     // TODO: Get rid of these hard-coded options.
-    std::string completions("-h -quiet -version -nocopyright");
+    std::string completions("-h\\n-quiet\\n-version\\n-nocopyright");
     for (ModuleNameList::const_iterator i = modules.begin();
          i != modules.end(); ++i)
     {
-        completions.append(" ");
+        completions.append("\\n");
         completions.append(*i);
     }
-    impl_->file_->writeLine("COMPREPLY=( $(compgen -'" + completions + "' -- $c) )");
+    impl_->file_->writeLine("COMPREPLY=( $(compgen -S ' ' -W $'" + completions + "' -- $c) )");
     impl_->file_->writeLine("return 0");
     impl_->file_->writeLine("fi");
     impl_->file_->writeLine("m=${COMP_WORDS[i]}");
index 3dc34fc9862e4b0af63c14eefe0ff95474de2cf7..6cfb77873aa0154903c185c9a5db2e0b506392c9 100644 (file)
@@ -53,7 +53,7 @@ elseif(GMX_BUILD_MDRUN_ONLY)
         COMPILE_FLAGS "${OpenMP_C_FLAGS}")
     install(TARGETS mdrun DESTINATION ${BIN_INSTALL_DIR} COMPONENT mdrun)
     file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gmx-completion-${BINARY_NAME}.bash
-         "complete -F _gmx_mdrun_compl ${BINARY_NAME}")
+         "complete -o nospace -F _gmx_mdrun_compl ${BINARY_NAME}")
     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/gmx-completion-${BINARY_NAME}.bash
             DESTINATION ${BIN_INSTALL_DIR} COMPONENT runtime)
 else()
@@ -109,7 +109,7 @@ else()
         install(DIRECTORY ${COMPLETION_DIR}/
                 DESTINATION ${BIN_INSTALL_DIR} COMPONENT runtime)
         file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/gmx-completion-${BINARY_NAME}.bash
-             "complete -F _gmx_compl ${BINARY_NAME}")
+             "complete -o nospace -F _gmx_compl ${BINARY_NAME}")
         install(FILES ${CMAKE_CURRENT_BINARY_DIR}/gmx-completion-${BINARY_NAME}.bash
                 DESTINATION ${BIN_INSTALL_DIR} COMPONENT runtime)
     endif()