Merge branch release-5-1
[alexxy/gromacs.git] / src / gromacs / commandline / shellcompletions.cpp
index 9697a5cbfe364d75a97815100d90c791cb373a19..520ff469092dfc79a25b7095c878ff35b6aaed9f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
  * Copyright (c) 2001-2004, The GROMACS development team.
- * Copyright (c) 2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
  * 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/utility/textwriter.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++)
-    {
-        const int   ftp          = tfn[i].ftp;
-        const char *multiplicity = "(( $n == 1 )) && ";
-        if (tfn[i].flag & ffMULT)
-        {
-            multiplicity = "";
-        }
-        if (ftp == efRND)
+    public:
+        const std::string &optionList() const { return optionList_; }
+
+        virtual void visitSubSection(const Options &section)
         {
-            fprintf(fp, "%s) %sCOMPREPLY=( $(compgen -S ' ' -d $c) );;\n",
-                    tfn[i].opt, multiplicity);
-            continue;
+            OptionsIterator iterator(section);
+            iterator.acceptSubSections(this);
+            iterator.acceptOptions(this);
         }
-        fprintf(fp, "%s) %sCOMPREPLY=( $(compgen -S ' ' -X '!*.",
-                tfn[i].opt, multiplicity);
-        const int genericCount = ftp2generic_count(ftp);
-        if (genericCount > 0)
+        virtual void visitOption(const OptionInfo &option)
         {
-            fprintf(fp, "@(");
-            const int *const genericTypes = ftp2generic_list(ftp);
-            for (int j = 0; j < genericCount; j++)
+            if (option.isHidden())
             {
-                if (j > 0)
-                {
-                    fprintf(fp, "|");
-                }
-                fprintf(fp, "%s", ftp2ext(genericTypes[j]));
+                return;
             }
-            fprintf(fp, ")");
-        }
-        else
-        {
-            fprintf(fp, "%s", ftp2ext(ftp));
-        }
-        fprintf(fp, "?(");
-        for (int j = 0; j < NZEXT; j++)
-        {
-            if (j > 0)
+            if (!optionList_.empty())
             {
-                fprintf(fp, "|");
+                optionList_.append("\\n");
             }
-            fprintf(fp, "%s", z_ext[j]);
+            optionList_.append("-");
+            const BooleanOptionInfo *booleanOption
+                = option.toType<BooleanOptionInfo>();
+            if (booleanOption != NULL && booleanOption->defaultValue())
+            {
+                optionList_.append("no");
+            }
+            optionList_.append(option.name());
         }
-        fprintf(fp, ")' -f $c ; compgen -S '/' -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
 {
-    fprintf(fp, "if (( $COMP_CWORD <= 1 )) || [[ $c == -* ]]; then COMPREPLY=( $(compgen -S ' '  -W $'");
-    const char *sep = "";
-    for (int i = 0; i < nfile; i++)
+    public:
+        explicit OptionCompletionWriter(TextWriter *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);
+
+        TextWriter &out_;
+};
+
+void OptionCompletionWriter::visitOption(const OptionInfo &option)
+{
+    if (option.isHidden())
     {
-        fprintf(fp, "%s-%s", sep, fnm[i].opt+1);
-        sep = "\\n";
+        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, "%s-no%s", sep, pa[i].option + 1);
+            writeOptionCompletion(option, "compgen -S ' ' -d $c");
+            return;
         }
-        else
+        const FileNameOptionInfo::ExtensionList &extensionList = fileOption->extensions();
+        if (extensionList.empty())
         {
-            fprintf(fp, "%s-%s", sep, pa[i].option + 1);
+            return;
         }
-        sep = "\\n";
-    }
-    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) (( $n == 1 )) && COMPREPLY=( $(compgen -S ' ' -W $'", pa[i].option);
-            for (int j = 1; pa[i].u.c[j]; j++)
-            {
-                fprintf(fp, "%s%s", (j == 1 ? "" : "\\n"), 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 IFS=$'\\n'\n");
-    fprintf(out, "local c=${COMP_WORDS[COMP_CWORD]}\n");
-    fprintf(out, "local n\n");
-    fprintf(out, "for ((n=1;n<COMP_CWORD;++n)) ; do [[ \"${COMP_WORDS[COMP_CWORD-n]}\" == -* ]] && break ; done\n");
-    fprintf(out, "local p=${COMP_WORDS[COMP_CWORD-n]}\n");
-    fprintf(out, "COMPREPLY=()\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
 {
@@ -189,8 +196,8 @@ class ShellCompletionWriter::Impl
             return formatString("_%s_%s_compl", binaryName_.c_str(), moduleName);
         }
 
-        std::string             binaryName_;
-        boost::scoped_ptr<File> file_;
+        std::string                   binaryName_;
+        boost::scoped_ptr<TextWriter> file_;
 };
 
 ShellCompletionWriter::ShellCompletionWriter(const std::string     &binaryName,
@@ -203,55 +210,41 @@ ShellCompletionWriter::~ShellCompletionWriter()
 {
 }
 
-File *ShellCompletionWriter::outputFile()
+TextOutputStream &ShellCompletionWriter::outputStream()
 {
-    return impl_->file_.get();
+    return impl_->file_->stream();
 }
 
 void ShellCompletionWriter::startCompletions()
 {
-    impl_->file_.reset(new File(impl_->binaryName_ + "-completion.bash", "w"));
-    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);
+    impl_->file_.reset(new TextWriter(impl_->binaryName_ + "-completion.bash"));
 }
 
 void ShellCompletionWriter::writeModuleCompletions(
         const char    *moduleName,
-        const Options  & /*options*/)
+        const Options &options)
 {
-    // TODO: Implement.
-    impl_->file_->writeLine(
-            impl_->completionFunctionName(moduleName) + "() {\nCOMPREPLY=()\n}\n");
+    TextWriter &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");
@@ -264,8 +257,9 @@ void ShellCompletionWriter::writeWrapperCompletions(
     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\\n-quiet\\n-version\\n-nocopyright");
+    OptionsListWriter lister;
+    lister.visitSubSection(options);
+    std::string       completions(lister.optionList());
     for (ModuleNameList::const_iterator i = modules.begin();
          i != modules.end(); ++i)
     {