Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / commandline / shellcompletions.cpp
index cf3cd9fb1d0bb453de0e9e1a589bec45c4ded398..1eefeda1cf6fd282965b63511aab391030368ca4 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#include "gromacs/commandline/shellcompletions.h"
+/*! \internal \file
+ * \brief
+ * Implements gmx::ShellCompletionWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#include "gmxpre.h"
+
+#include "shellcompletions.h"
 
 #include <cstdio>
 
 #include "gromacs/commandline/cmdlinehelpcontext.h"
 #include "gromacs/commandline/pargs.h"
 #include "gromacs/fileio/filenm.h"
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/optionsvisitor.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
-#include "gromacs/legacyheaders/smalloc.h"
+namespace gmx
+{
 
-// TODO: Don't duplicate this from filenm.c/futil.c.
-#define NZEXT 2
-static const char *z_ext[NZEXT] = { ".gz", ".Z" };
+namespace
+{
 
-static void pr_fopts(FILE *fp, int nf, const t_filenm tfn[])
+class OptionsListWriter : public OptionsVisitor
 {
-    for (int i = 0; i < nf; i++)
-    {
-        fprintf(fp, "%s) COMPREPLY=( $(compgen -X '!*.", tfn[i].opt);
-        const int ftp          = tfn[i].ftp;
-        const int genericCount = ftp2generic_count(ftp);
-        if (genericCount > 0)
-        {
-            fprintf(fp, "+(");
-            const int *const genericTypes = ftp2generic_list(ftp);
-            for (int j = 0; j < genericCount; j++)
-            {
-                if (j > 0)
-                {
-                    fprintf(fp, "|");
-                }
-                fprintf(fp, "%s", ftp2ext(genericTypes[j]));
-            }
-            fprintf(fp, ")");
-        }
-        else
+    public:
+        const std::string &optionList() const { return optionList_; }
+
+        virtual void visitSubSection(const Options &section)
         {
-            fprintf(fp, "%s", ftp2ext(ftp));
+            OptionsIterator iterator(section);
+            iterator.acceptSubSections(this);
+            iterator.acceptOptions(this);
         }
-        fprintf(fp, "*(");
-        for (int j = 0; j < NZEXT; j++)
+        virtual void visitOption(const OptionInfo &option)
         {
-            if (j > 0)
+            if (option.isHidden())
+            {
+                return;
+            }
+            if (!optionList_.empty())
+            {
+                optionList_.append("\\n");
+            }
+            optionList_.append("-");
+            const BooleanOptionInfo *booleanOption
+                = option.toType<BooleanOptionInfo>();
+            if (booleanOption != NULL && booleanOption->defaultValue())
             {
-                fprintf(fp, "|");
+                optionList_.append("no");
             }
-            fprintf(fp, "%s", z_ext[j]);
+            optionList_.append(option.name());
         }
-        fprintf(fp, ")' -f $c ; compgen -S '/' -X '.*' -d $c ));;\n");
-    }
-}
 
-static void pr_opts(FILE *fp,
-                    int nfile,  t_filenm *fnm,
-                    int npargs, t_pargs pa[])
+    private:
+        std::string optionList_;
+};
+
+class OptionCompletionWriter : public OptionsVisitor
+{
+    public:
+        explicit OptionCompletionWriter(File *out) : out_(*out) {}
+
+        virtual void visitSubSection(const Options &section)
+        {
+            OptionsIterator iterator(section);
+            iterator.acceptSubSections(this);
+            iterator.acceptOptions(this);
+        }
+        virtual void visitOption(const OptionInfo &option);
+
+    private:
+        void writeOptionCompletion(const OptionInfo  &option,
+                                   const std::string &completion);
+
+        File &out_;
+};
+
+void OptionCompletionWriter::visitOption(const OptionInfo &option)
 {
-    fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen  -W '");
-    for (int i = 0; i < nfile; i++)
+    if (option.isHidden())
     {
-        fprintf(fp, " -%s", fnm[i].opt+1);
+        return;
     }
-    for (int i = 0; i < npargs; i++)
+    const FileNameOptionInfo *fileOption = option.toType<FileNameOptionInfo>();
+    if (fileOption != NULL)
     {
-        if (pa[i].type == etBOOL && *(pa[i].u.b))
+        if (fileOption->isDirectoryOption())
         {
-            fprintf(fp, " -no%s", pa[i].option+1);
+            writeOptionCompletion(option, "compgen -S ' ' -d $c");
+            return;
         }
-        else
+        const FileNameOptionInfo::ExtensionList &extensionList = fileOption->extensions();
+        if (extensionList.empty())
         {
-            fprintf(fp, " -%s", pa[i].option+1);
+            return;
         }
-    }
-    fprintf(fp, "' -- $c)); return 0; fi\n");
-}
-
-static void pr_enums(FILE *fp, int npargs, t_pargs pa[])
-{
-    for (int i = 0; i < npargs; i++)
-    {
-        if (pa[i].type == etENUM)
+        std::string completion("compgen -S ' ' -X '!*");
+        std::string extensions(joinStrings(extensionList, "|"));
+        if (extensionList.size() > 1)
         {
-            fprintf(fp, "%s) COMPREPLY=( $(compgen -W '", pa[i].option);
-            for (int j = 1; pa[i].u.c[j]; j++)
-            {
-                fprintf(fp, " %s", pa[i].u.c[j]);
-            }
-            fprintf(fp, " ' -- $c ));;\n");
+            extensions = "@(" + extensions + ")";
         }
+        completion.append(extensions);
+        // TODO: Don't duplicate this from filenm.c/futil.c.
+        completion.append("?(.gz|.Z)' -f -- $c ; compgen -S '/' -d $c");
+        writeOptionCompletion(option, completion);
+        return;
+    }
+    const StringOptionInfo *stringOption = option.toType<StringOptionInfo>();
+    if (stringOption != NULL && stringOption->isEnumerated())
+    {
+        std::string completion("compgen -S ' ' -W $'");
+        completion.append(joinStrings(stringOption->allowedValues(), "\\n"));
+        completion.append("' -- $c");
+        writeOptionCompletion(option, completion);
+        return;
     }
 }
 
-static void write_bashcompl(FILE *out,
-                            const char *funcName,
-                            int nfile,  t_filenm *fnm,
-                            int npargs, t_pargs *pa)
+void OptionCompletionWriter::writeOptionCompletion(
+        const OptionInfo &option, const std::string &completion)
 {
-    /* Advanced bash completions are handled by shell functions.
-     * p and c hold the previous and current word on the command line.
-     */
-    fprintf(out, "%s() {\n", funcName);
-    fprintf(out, "local p c\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");
-
-    pr_enums(out, npargs, pa);
-    pr_fopts(out, nfile, fnm);
-    fprintf(out, "esac }\n");
+    std::string result(formatString("-%s) ", option.name().c_str()));
+    if (option.maxValueCount() >= 0)
+    {
+        result.append(formatString("(( $n <= %d )) && ", option.maxValueCount()));
+    }
+    result.append("COMPREPLY=( $(");
+    result.append(completion);
+    result.append("));;");
+    out_.writeLine(result);
 }
 
-namespace gmx
-{
+}   // namespace
 
 class ShellCompletionWriter::Impl
 {
@@ -194,64 +221,53 @@ void ShellCompletionWriter::startCompletions()
     impl_->file_->writeLine("shopt -s extglob");
 }
 
-void ShellCompletionWriter::writeLegacyModuleCompletions(
-        const char *moduleName,
-        int nfile,  t_filenm *fnm,
-        int npargs, t_pargs *pa)
-{
-    int      npar;
-    t_pargs *par;
-
-    // Remove hidden arguments.
-    snew(par, npargs);
-    npar = 0;
-    for (int i = 0; i < npargs; i++)
-    {
-        if (!is_hidden(&pa[i]))
-        {
-            par[npar] = pa[i];
-            npar++;
-        }
-    }
-
-    write_bashcompl(impl_->file_->handle(),
-                    impl_->completionFunctionName(moduleName).c_str(),
-                    nfile, fnm, npar, par);
-
-    sfree(par);
-}
-
 void ShellCompletionWriter::writeModuleCompletions(
         const char    *moduleName,
-        const Options  & /*options*/)
+        const Options &options)
 {
-    // TODO: Implement.
-    impl_->file_->writeLine(
-            impl_->completionFunctionName(moduleName) + "() {\nCOMPREPLY=()\n}\n");
+    File &out = *impl_->file_;
+    out.writeLine(formatString("%s() {", impl_->completionFunctionName(moduleName).c_str()));
+    out.writeLine("local IFS=$'\\n'");
+    out.writeLine("local c=${COMP_WORDS[COMP_CWORD]}");
+    out.writeLine("local n");
+    out.writeLine("for ((n=1;n<COMP_CWORD;++n)) ; do [[ \"${COMP_WORDS[COMP_CWORD-n]}\" == -* ]] && break ; done");
+    out.writeLine("local p=${COMP_WORDS[COMP_CWORD-n]}");
+    out.writeLine("COMPREPLY=()");
+
+    OptionsListWriter listWriter;
+    listWriter.visitSubSection(options);
+    out.writeLine(formatString("if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -S ' '  -W $'%s' -- $c)); return 0; fi", listWriter.optionList().c_str()));
+
+    out.writeLine("case \"$p\" in");
+    OptionCompletionWriter optionWriter(&out);
+    optionWriter.visitSubSection(options);
+    out.writeLine("esac }");
 }
 
 void ShellCompletionWriter::writeWrapperCompletions(
-        const ModuleNameList &modules)
+        const ModuleNameList &modules, const Options &options)
 {
     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");
-    impl_->file_->writeLine("if [[ \"${COMP_WORDS[i]}\" != -* ]]; then break ; fi");
+    impl_->file_->writeLine("[[ \"${COMP_WORDS[i]}\" != -* ]] && break");
     impl_->file_->writeLine("unset COMP_WORDS[i]");
     impl_->file_->writeLine("done");
     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");
+    OptionsListWriter lister;
+    lister.visitSubSection(options);
+    std::string       completions(lister.optionList());
     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]}");