Rewrite help output from Options
authorTeemu Murtola <teemu.murtola@gmail.com>
Thu, 17 Oct 2013 03:18:47 +0000 (06:18 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Mon, 20 Jan 2014 17:45:12 +0000 (18:45 +0100)
(Partially) rewrite the way command-line help is printed for an Options
object.  Now man pages and HTML output are supported in addition to
console output.  The console output is formatted in a simpler manner,
since there is no longer any need to cater for user-provided values in
the output.  There is some extra machinery now in cmdlinehelpwriter.cpp,
but left it there as it may still be useful in the future, and it does
structure the code a bit more cleanly.

As an extra bonus, the commandline module should no longer depend on the
selection module.

Things more or less work, but the following could still be addressed,
either in this change or later:
 - Re-add selection option help formatting tests somewhere else.
 - Add unit tests for various things about non-console help formatting.
 - Refactor the way the help information is fetched from OptionInfo
   objects: it would be cleaner to have a single method that returns all
   the information in a struct, instead of multiple virtual methods that
   return different pieces of information, many of which are not
   required for anything else except the help output.
 - Add proper synopsis that lists all the options.

Part of #969.

Change-Id: Iadf1b5df15f278547d5db6407f3e3c689fb88645

30 files changed:
src/gromacs/commandline/CMakeLists.txt
src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/commandline/tests/cmdlinehelpwriter.cpp
src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesLongFileOptions.xml
src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesLongOptions.xml
src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesMultipleSections.xml
src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesOptionTypes.xml
src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesSelectionOptions.xml [deleted file]
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/onlinehelp/helpformat.h
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/onlinehelp/helpwritercontext.h
src/gromacs/onlinehelp/tests/helpformat.cpp
src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesLastColumnFolding.xml [new file with mode: 0644]
src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesOverflowingLines.xml
src/gromacs/options/abstractoption.cpp
src/gromacs/options/abstractoption.h
src/gromacs/options/abstractoptionstorage.h
src/gromacs/options/basicoptions.cpp
src/gromacs/options/basicoptionstorage.h
src/gromacs/options/filenameoption.cpp
src/gromacs/options/filenameoptionstorage.h
src/gromacs/options/optionstoragetemplate.h
src/gromacs/options/tests/abstractoptionstorage.cpp
src/gromacs/selection/selectionfileoptionstorage.h
src/gromacs/selection/selectionoptionstorage.h
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/trajectoryanalysis/modules/angle.cpp
src/gromacs/trajectoryanalysis/modules/freevolume.cpp
src/gromacs/trajectoryanalysis/runnercommon.cpp

index a288ef34b68d8f1d579d7cd1498fb10094b09db9..1fcb8af1a7aca1a1866035f0e07f1b7f5487bab8 100644 (file)
@@ -41,8 +41,6 @@ set(COMMANDLINE_PUBLIC_HEADERS
     pargs.h)
 gmx_install_headers(commandline ${COMMANDLINE_PUBLIC_HEADERS})
 
-# mdrun build does not include the selection machinery, which is used in the
-# tests
-if (BUILD_TESTING AND NOT GMX_BUILD_MDRUN_ONLY)
+if (BUILD_TESTING)
     add_subdirectory(tests)
 endif()
index 778c0a1b2a4a2fc88846a15f6bbb36c8177a5d36..1144f1e54fad50bf47c503d10e251072926b6a67 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -43,6 +43,8 @@
 
 #include <string>
 
+#include <boost/scoped_ptr.hpp>
+
 #include "gromacs/commandline/cmdlinehelpcontext.h"
 #include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
@@ -51,8 +53,6 @@
 #include "gromacs/options/options.h"
 #include "gromacs/options/optionsvisitor.h"
 #include "gromacs/options/timeunitmanager.h"
-#include "gromacs/selection/selectionfileoption.h"
-#include "gromacs/selection/selectionoption.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/stringutil.h"
@@ -63,6 +63,9 @@ namespace gmx
 namespace
 {
 
+//! \addtogroup module_commandline
+//! \{
+
 /********************************************************************
  * OptionsFormatterInterface
  */
@@ -71,8 +74,6 @@ namespace
  * Interface for output format specific formatting of options.
  *
  * \see OptionsFilter
- *
- * \ingroup module_commandline
  */
 class OptionsFormatterInterface
 {
@@ -88,9 +89,6 @@ class OptionsFormatterInterface
         //! Formats a single non-file, non-selection option.
         virtual void formatOption(const HelpWriterContext &context,
                                   const OptionInfo        &option) = 0;
-        //! Formats a single selection option.
-        virtual void formatSelectionOption(const HelpWriterContext &context,
-                                           const OptionInfo        &option) = 0;
 };
 
 /********************************************************************
@@ -105,8 +103,6 @@ class OptionsFormatterInterface
  * An object implementing the OptionsFormatterInterface must be provided to the
  * constructor, and does the actual formatting that is specific to the output
  * format.
- *
- * \ingroup module_commandline
  */
 class OptionsFilter : public OptionsVisitor
 {
@@ -116,7 +112,6 @@ class OptionsFilter : public OptionsVisitor
         {
             eSelectDescriptions,
             eSelectFileOptions,
-            eSelectSelectionOptions,
             eSelectOtherOptions
         };
 
@@ -131,8 +126,8 @@ class OptionsFilter : public OptionsVisitor
         OptionsFilter(const HelpWriterContext   &context,
                       OptionsFormatterInterface *formatter)
             : context_(context), formatter_(*formatter),
-              filterType_(eSelectOtherOptions), bShowHidden_(false),
-              bDidOutput_(false)
+              bShowHidden_(false), filterType_(eSelectOtherOptions),
+              title_(NULL), bDidOutput_(false)
         {
         }
 
@@ -143,26 +138,44 @@ class OptionsFilter : public OptionsVisitor
         }
 
         //! Formats selected options using the formatter.
-        void formatSelected(FilterType type, const Options &options);
+        void formatSelected(FilterType type, const Options &options,
+                            const char *title);
 
         virtual void visitSubSection(const Options &section);
         virtual void visitOption(const OptionInfo &option);
 
     private:
+        void writeTitleIfSet()
+        {
+            if (title_ != NULL)
+            {
+                context_.writeTitle(title_);
+            }
+        }
+
         const HelpWriterContext        &context_;
         OptionsFormatterInterface      &formatter_;
-        FilterType                      filterType_;
         bool                            bShowHidden_;
+        FilterType                      filterType_;
+        const char                     *title_;
         bool                            bDidOutput_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
 };
 
-void OptionsFilter::formatSelected(FilterType type, const Options &options)
+void OptionsFilter::formatSelected(FilterType type, const Options &options,
+                                   const char *title)
 {
     filterType_ = type;
+    title_      = title;
     bDidOutput_ = false;
     visitSubSection(options);
     if (bDidOutput_)
     {
+        if (type != eSelectDescriptions)
+        {
+            context_.writeOptionListEnd();
+        }
         context_.outputFile().writeLine();
     }
 }
@@ -171,12 +184,19 @@ void OptionsFilter::visitSubSection(const Options &section)
 {
     if (filterType_ == eSelectDescriptions)
     {
-        if (bDidOutput_)
+        if (!section.description().empty())
         {
-            context_.outputFile().writeLine();
+            if (bDidOutput_)
+            {
+                context_.outputFile().writeLine();
+            }
+            else
+            {
+                writeTitleIfSet();
+            }
+            formatter_.formatDescription(context_, section);
+            bDidOutput_ = true;
         }
-        formatter_.formatDescription(context_, section);
-        bDidOutput_ = true;
     }
 
     OptionsIterator iterator(section);
@@ -194,24 +214,24 @@ void OptionsFilter::visitOption(const OptionInfo &option)
     {
         if (filterType_ == eSelectFileOptions)
         {
+            if (!bDidOutput_)
+            {
+                writeTitleIfSet();
+                context_.writeOptionListStart();
+            }
             formatter_.formatFileOption(context_,
                                         *option.toType<FileNameOptionInfo>());
             bDidOutput_ = true;
         }
         return;
     }
-    if (option.isType<SelectionFileOptionInfo>()
-        || option.isType<SelectionOptionInfo>())
+    if (filterType_ == eSelectOtherOptions)
     {
-        if (filterType_ == eSelectSelectionOptions)
+        if (!bDidOutput_)
         {
-            formatter_.formatSelectionOption(context_, option);
-            bDidOutput_ = true;
+            writeTitleIfSet();
+            context_.writeOptionListStart();
         }
-        return;
-    }
-    if (filterType_ == eSelectOtherOptions)
-    {
         formatter_.formatOption(context_, option);
         bDidOutput_ = true;
         return;
@@ -234,110 +254,41 @@ class CommonFormatterData
 };
 
 /********************************************************************
- * OptionsConsoleFormatter
+ * Helper functions
  */
 
-/*! \brief
- * Formatter implementation for console help.
- *
- * \ingroup module_commandline
- */
-class OptionsConsoleFormatter : public OptionsFormatterInterface
-{
-    public:
-        //! Creates a helper object for formatting options help for console.
-        explicit OptionsConsoleFormatter(const CommonFormatterData &common);
-
-        virtual void formatDescription(const HelpWriterContext &context,
-                                       const Options           &section);
-        virtual void formatFileOption(const HelpWriterContext  &context,
-                                      const FileNameOptionInfo &option);
-        virtual void formatOption(const HelpWriterContext &context,
-                                  const OptionInfo        &option);
-        virtual void formatSelectionOption(const HelpWriterContext &context,
-                                           const OptionInfo        &option);
-
-    private:
-        const CommonFormatterData &common_;
-        TextTableFormatter         fileOptionFormatter_;
-        TextTableFormatter         genericOptionFormatter_;
-        TextTableFormatter         selectionOptionFormatter_;
-};
-
-OptionsConsoleFormatter::OptionsConsoleFormatter(const CommonFormatterData &common)
-    : common_(common)
+//! Formats the default option value as a string.
+std::string
+defaultOptionValue(const OptionInfo &option)
 {
-    fileOptionFormatter_.addColumn("Option",      6, false);
-    fileOptionFormatter_.addColumn("Filename",    12, false);
-    fileOptionFormatter_.addColumn("Type",        12, false);
-    fileOptionFormatter_.addColumn("Description", 45, true);
-
-    genericOptionFormatter_.addColumn("Option",      12, false);
-    genericOptionFormatter_.addColumn("Type",         6, false);
-    genericOptionFormatter_.addColumn("Value",        6, false);
-    genericOptionFormatter_.addColumn("Description", 51, true);
-
-    selectionOptionFormatter_.addColumn("Selection",   10, false);
-    selectionOptionFormatter_.addColumn("Description", 67, true);
-}
-
-void OptionsConsoleFormatter::formatDescription(
-        const HelpWriterContext &context, const Options &section)
-{
-    if (!section.description().empty())
+    if (option.valueCount() == 0
+        || (option.valueCount() == 1 && option.formatValue(0).empty()))
     {
-        File              &file  = context.outputFile();
-        const std::string &title = section.title();
-        if (!title.empty())
-        {
-            file.writeLine(title);
-            file.writeLine();
-        }
-        context.writeTextBlock(section.description());
+        return option.formatDefaultValueIfSet();
     }
-}
-
-void OptionsConsoleFormatter::formatFileOption(
-        const HelpWriterContext &context, const FileNameOptionInfo &option)
-{
-    int firstShortValue = 0;  // The first value after which the type fits.
-    int firstLongValue  = -1; // First value that overlaps description column.
-    int lastLongValue   = -1; // Last value like the above.
-
-    // Get the values to write and check where text overflows the columns.
-    fileOptionFormatter_.clear();
-    std::string name(formatString("-%s", option.name().c_str()));
-    fileOptionFormatter_.addColumnLine(0, name);
-    for (int i = 0; i < option.valueCount() || i == 0; ++i)
+    else
     {
-        std::string value;
-        if (option.valueCount() == 0
-            || (option.valueCount() == 1 && option.formatValue(0).empty()))
-        {
-            value = option.formatDefaultValueIfSet();
-        }
-        else
-        {
-            value = option.formatValue(i);
-        }
-        fileOptionFormatter_.addColumnLine(1, value);
-        if (value.length() > 12U && i == firstShortValue)
-        {
-            firstShortValue = i + 1;
-        }
-        if (value.length() > 25U)
+        std::string result;
+        for (int i = 0; i < option.valueCount(); ++i)
         {
-            if (firstLongValue == -1)
+            if (i != 0)
             {
-                firstLongValue = i;
+                result.append(" ");
             }
-            lastLongValue = i;
+            result.append(option.formatValue(i));
         }
+        return result;
     }
+}
+
+//! Formats the flags for a file option as a string.
+std::string
+fileOptionFlagsAsString(const FileNameOptionInfo &option, bool bAbbrev)
+{
     std::string type;
     if (option.isInputOutputFile())
     {
-        type = "In/Out";
+        type = bAbbrev ? "In/Out" : "Input/Output";
     }
     else if (option.isInputFile())
     {
@@ -349,127 +300,151 @@ void OptionsConsoleFormatter::formatFileOption(
     }
     if (!option.isRequired())
     {
-        type += ", Opt.";
+        type += bAbbrev ? ", Opt." : ", Optional";
     }
     if (option.isLibraryFile())
     {
-        type += ", Lib.";
+        type += bAbbrev ? ", Lib." : ", Library";
     }
-    bool bLongType = (type.length() > 12U);
-    fileOptionFormatter_.addColumnLine(2, type);
-    fileOptionFormatter_.addColumnHelpTextBlock(3, context, option.description());
+    // TODO: Add a tag for options that accept multiple files.
+    return type;
+}
 
-    // Compute layout.
-    if (name.length() > 6U || firstShortValue > 0)
-    {
-        fileOptionFormatter_.setColumnFirstLineOffset(1, 1);
-        // Assume that the name is <20 chars, so that the type fits
-        if (firstLongValue >= 0)
-        {
-            ++firstLongValue;
-            ++lastLongValue;
-        }
-    }
-    int firstDescriptionLine = 0;
-    if (bLongType)
-    {
-        firstDescriptionLine = 1;
-    }
-    fileOptionFormatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
-    if (firstLongValue >= 0
-        && fileOptionFormatter_.lastColumnLine(3) >= firstLongValue)
+//! Formats the description for an option as a string.
+std::string
+descriptionWithOptionDetails(const CommonFormatterData &common,
+                             const OptionInfo          &option)
+{
+    std::string             description(option.formatDescription());
+
+    const FloatOptionInfo  *floatOption  = option.toType<FloatOptionInfo>();
+    const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
+    if ((floatOption != NULL && floatOption->isTime())
+        || (doubleOption != NULL && doubleOption->isTime()))
     {
-        firstDescriptionLine = lastLongValue + 1;
-        fileOptionFormatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
+        // TODO: It could be nicer to have this in basicoptions.cpp.
+        description = replaceAll(description, "%t", common.timeUnit);
     }
 
-    // Do the formatting.
-    context.outputFile().writeString(fileOptionFormatter_.formatRow());
+    return description;
 }
 
-void OptionsConsoleFormatter::formatOption(
-        const HelpWriterContext &context, const OptionInfo &option)
+/********************************************************************
+ * OptionsExportFormatter
+ */
+
+/*! \brief
+ * Formatter implementation for help export.
+ */
+class OptionsExportFormatter : public OptionsFormatterInterface
 {
-    genericOptionFormatter_.clear();
-    bool        bIsBool = option.isType<BooleanOptionInfo>();
-    std::string name(formatString("-%s%s", bIsBool ? "[no]" : "",
-                                  option.name().c_str()));
-    genericOptionFormatter_.addColumnLine(0, name);
-    genericOptionFormatter_.addColumnLine(1, option.type());
-    if (name.length() > 12U)
-    {
-        genericOptionFormatter_.setColumnFirstLineOffset(1, 1);
-    }
+    public:
+        //! Creates a helper object for formatting options.
+        OptionsExportFormatter(const CommonFormatterData &common, bool bConsole);
+
+        virtual void formatDescription(const HelpWriterContext &context,
+                                       const Options           &section);
+        virtual void formatFileOption(const HelpWriterContext  &context,
+                                      const FileNameOptionInfo &option);
+        virtual void formatOption(const HelpWriterContext &context,
+                                  const OptionInfo        &option);
+
+    private:
+        const CommonFormatterData             &common_;
+        boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
 
-    // TODO: Better handling of multiple long values
-    std::string values;
-    for (int i = 0; i < option.valueCount(); ++i)
+        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsExportFormatter);
+};
+
+OptionsExportFormatter::OptionsExportFormatter(
+        const CommonFormatterData &common, bool bConsole)
+    : common_(common)
+{
+    if (bConsole)
     {
-        if (i != 0)
-        {
-            values.append(" ");
-        }
-        values.append(option.formatValue(i));
+        consoleFormatter_.reset(new TextTableFormatter());
+        consoleFormatter_->setFirstColumnIndent(1);
+        consoleFormatter_->setFoldLastColumnToNextLine(4);
+        consoleFormatter_->addColumn(NULL, 6, false);
+        consoleFormatter_->addColumn(NULL, 8, false);
+        consoleFormatter_->addColumn(NULL, 10, false);
+        consoleFormatter_->addColumn(NULL, 50, true);
     }
-    genericOptionFormatter_.addColumnLine(2, values);
+}
 
-    std::string             description(option.description());
-    const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
-    if (doubleOption != NULL && doubleOption->isTime())
+void OptionsExportFormatter::formatDescription(
+        const HelpWriterContext &context, const Options &section)
+{
+    // TODO: Print title for the section?
+    context.writeTextBlock(section.description());
+}
+
+void OptionsExportFormatter::formatFileOption(
+        const HelpWriterContext &context, const FileNameOptionInfo &option)
+{
+    const bool  bAbbrev = (context.outputFormat() == eHelpOutputFormat_Console);
+    std::string value("<" + option.type() + ">");
+    std::string defaultValue(defaultOptionValue(option));
+    std::string info = "(" + fileOptionFlagsAsString(option, bAbbrev) + ")";
+    if (!defaultValue.empty())
     {
-        description = replaceAll(description, "%t", common_.timeUnit);
+        info = "(" + defaultValue + ") " + info;
     }
-    const StringOptionInfo *stringOption = option.toType<StringOptionInfo>();
-    if (stringOption != NULL && stringOption->isEnumerated())
+    std::string description(option.formatDescription());
+    if (context.outputFormat() == eHelpOutputFormat_Console)
     {
-        const std::vector<std::string> &allowedValues
-            = stringOption->allowedValues();
-        description.append(": ");
-        for (size_t i = 0; i < allowedValues.size(); ++i)
-        {
-            if (i > 0)
-            {
-                description.append(i + 1 < allowedValues.size()
-                                   ? ", " : ", or ");
-            }
-            description.append(allowedValues[i]);
-        }
+        consoleFormatter_->clear();
+        consoleFormatter_->addColumnLine(0, "-" + option.name());
+        consoleFormatter_->addColumnLine(1, value);
+        consoleFormatter_->addColumnLine(2, info);
+        consoleFormatter_->addColumnHelpTextBlock(3, context, description);
+        context.outputFile().writeString(consoleFormatter_->formatRow());
     }
-    genericOptionFormatter_.addColumnHelpTextBlock(3, context, description);
-    if (values.length() > 6U)
+    else
     {
-        genericOptionFormatter_.setColumnFirstLineOffset(3, 1);
+        value += " " + info;
+        context.writeOptionItem("-" + option.name(), value, description);
     }
-
-    context.outputFile().writeString(genericOptionFormatter_.formatRow());
 }
 
-void OptionsConsoleFormatter::formatSelectionOption(
+void OptionsExportFormatter::formatOption(
         const HelpWriterContext &context, const OptionInfo &option)
 {
-    File &file = context.outputFile();
-
-    selectionOptionFormatter_.clear();
-    std::string name(formatString("-%s", option.name().c_str()));
-    selectionOptionFormatter_.addColumnLine(0, name);
-    selectionOptionFormatter_.addColumnHelpTextBlock(1, context,
-                                                     option.description());
-    file.writeString(selectionOptionFormatter_.formatRow());
-
-    TextLineWrapper wrapper;
-    wrapper.settings().setLineLength(77);
-    wrapper.settings().setFirstLineIndent(4);
-    wrapper.settings().setIndent(8);
-    wrapper.settings().setContinuationChar('\\');
-    // TODO: What to do with selection variables?
-    // They are not printed as values for any option.
-    for (int i = 0; i < option.valueCount(); ++i)
+    std::string name(option.name());
+    std::string value("<" + option.type() + ">");
+    std::string defaultValue(defaultOptionValue(option));
+    std::string description(descriptionWithOptionDetails(common_, option));
+    if (option.isType<BooleanOptionInfo>())
+    {
+        name  = "[no]" + name;
+        // Old command-line parser doesn't accept any values for these.
+        // value = "[" + value + "]";
+        value.clear();
+    }
+    if (context.outputFormat() == eHelpOutputFormat_Console)
     {
-        std::string value(option.formatValue(i));
-        file.writeLine(wrapper.wrapToString(value));
+        consoleFormatter_->clear();
+        consoleFormatter_->addColumnLine(0, "-" + name);
+        consoleFormatter_->addColumnLine(1, value);
+        if (!defaultValue.empty())
+        {
+            consoleFormatter_->addColumnLine(2, "(" + defaultValue + ")");
+        }
+        consoleFormatter_->addColumnHelpTextBlock(3, context, description);
+        context.outputFile().writeString(consoleFormatter_->formatRow());
+    }
+    else
+    {
+        if (!defaultValue.empty())
+        {
+            value += " (" + defaultValue + ")";
+        }
+        context.writeOptionItem("-" + name, value, description);
     }
 }
 
+//! \}
+
 }   // namespace
 
 /********************************************************************
@@ -528,38 +503,32 @@ CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *time
 
 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
 {
-    boost::scoped_ptr<OptionsFormatterInterface> formatter;
-    const HelpWriterContext                     &writerContext = context.writerContext();
-    CommonFormatterData                          common(impl_->timeUnit_.c_str());
-    switch (writerContext.outputFormat())
+    const HelpWriterContext &writerContext = context.writerContext();
+    const bool               bConsole
+        = (writerContext.outputFormat() == eHelpOutputFormat_Console);
+    CommonFormatterData      common(impl_->timeUnit_.c_str());
+    OptionsExportFormatter   formatter(common, bConsole);
+    OptionsFilter            filter(writerContext, &formatter);
+    filter.setShowHidden(context.showHidden());
+
+    File &file = writerContext.outputFile();
+    if (!bConsole)
     {
-        case eHelpOutputFormat_Console:
-            formatter.reset(new OptionsConsoleFormatter(common));
-            break;
-        default:
-            // TODO: Implement once the situation with Redmine issue #969 is
-            // more clear.
-            GMX_THROW(NotImplementedError(
-                              "Command-line help is not implemented for this output format"));
+        // TODO: Write a proper synopsis, with all the options.
+        writerContext.writeTitle("Synopsis");
+        writerContext.writeTextBlock(context.moduleDisplayName());
+        file.writeLine("\n\n");
     }
-    OptionsFilter filter(writerContext, formatter.get());
-    filter.setShowHidden(context.showHidden());
 
     if (impl_->bShowDescriptions_)
     {
-        File &file = writerContext.outputFile();
-        file.writeLine("DESCRIPTION");
-        file.writeLine("-----------");
-        file.writeLine();
         filter.formatSelected(OptionsFilter::eSelectDescriptions,
-                              impl_->options_);
+                              impl_->options_, "Description");
     }
     filter.formatSelected(OptionsFilter::eSelectFileOptions,
-                          impl_->options_);
+                          impl_->options_, "File Options");
     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
-                          impl_->options_);
-    filter.formatSelected(OptionsFilter::eSelectSelectionOptions,
-                          impl_->options_);
+                          impl_->options_, "Options");
 }
 
 } // namespace gmx
index ca3f1b38c20fa9c61ca7da94b417052a24d471c8..1bad19b1b761d16b0a262edbcbdd35df5b02f321 100644 (file)
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/options.h"
-#include "gromacs/selection/selectioncollection.h"
-#include "gromacs/selection/selectionfileoption.h"
-#include "gromacs/selection/selectionoption.h"
-#include "gromacs/selection/selectionoptionmanager.h"
 #include "gromacs/utility/file.h"
 
 #include "testutils/stringtest.h"
@@ -143,9 +139,6 @@ TEST_F(CommandLineHelpWriterTest, HandlesOptionTypes)
                           .description("Output file description")
                           .filetype(eftPlot).outputFile());
 
-    options.addOption(SelectionFileOption("sf"));
-    options.addOption(SelectionOption("sel").description("Selection option"));
-
     CommandLineHelpWriter writer(options);
     bHidden_ = true;
     checkHelp(&writer);
@@ -217,9 +210,11 @@ TEST_F(CommandLineHelpWriterTest, HandlesLongOptions)
     checkHelp(&writer);
 }
 
-/*
+/* TODO: Add corresponding tests to either the selection module, or as part of
+ * trajectoryanalysis tests.
  * Tests help printing with selection options with values.
  */
+#if 0
 TEST_F(CommandLineHelpWriterTest, HandlesSelectionOptions)
 {
     using gmx::SelectionFileOption;
@@ -244,6 +239,7 @@ TEST_F(CommandLineHelpWriterTest, HandlesSelectionOptions)
     gmx::CommandLineHelpWriter writer(options);
     checkHelp(&writer);
 }
+#endif
 
 /*
  * Tests help printing for multiple sections.
index 21c7ec13d8325b2a7b7c4d5bc86492612f0646b0..6d547a729567e346bf592ff6a7b8df3bf9c999f5 100644 (file)
@@ -2,20 +2,17 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
-Option Filename     Type         Description
----------------------------------------------------------
--f                  Input        File name option with a long value
-       path/to/long/trajectory/name.xtc
--f2                 Input        File name option with a long value
-       path/to/long/trajectory.xtc
--lib                Input, Opt., Lib.
-       path/to/long/trajectory/name.xtc
-                                 File name option with a long value and type
--longfileopt        Input, Opt.  File name option with a long name
-       deffile.dat
--longfileopt2       Input, Opt., Lib.
-       path/to/long/file/name.dat
-                                 File name option with multiple long fields
+FILE OPTIONS
+
+ -f     <.xtc/.trr/...> (path/to/long/trajectory/name.xtc) (Input)
+     File name option with a long value: xtc trr cpt trj gro g96 pdb g87
+ -f2    <.xtc/.trr/...> (path/to/long/trajectory.xtc) (Input)
+     File name option with a long value: xtc trr cpt trj gro g96 pdb g87
+ -lib   <.xtc/.trr/...> (path/to/long/trajectory/name.xtc) (Input, Opt., Lib.) File name option with a long value and type: xtc trr cpt trj gro g96 pdb g87
+ -longfileopt <.dat> (deffile.dat) (Input, Opt.)
+     File name option with a long name
+ -longfileopt2 <.dat> (path/to/long/file/name.dat) (Input, Opt., Lib.)
+     File name option with multiple long fields
 
 ]]></String>
 </ReferenceData>
index fe9d7d089a130d7ff4edbff3b165eac312dbb8ec..56311fb12d1b4acafcd90083bed3d8b68ba6acec 100644 (file)
@@ -2,16 +2,13 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
-Option       Type   Value  Description
----------------------------------------------------
--[no]longboolean    yes    Boolean option with a long name
-             bool
--dvec        vector 1.135 2.32 3.2132
-                           Double vector option
--string      string A very long string value that overflows even the description column Another very long string value that overflows even the description column
-                           String option with very long values (may be less
-                           relevant with selections having their own option
-                           type)
+OPTIONS
+
+ -[no]longboolean (yes)     Boolean option with a long name
+ -dvec  <vector> (1.135 2.32 3.2132) Double vector option
+ -string <string> (A very long string value that overflows even the description column Another very long string value that overflows even the description column)
+     String option with very long values (may be less relevant with selections
+     having their own option type)
 
 ]]></String>
 </ReferenceData>
index d9872f781943e71ba72d4a315c18e8675131fa2b..08132448c4ed6612038dbf89d935f50f6e02fd22 100644 (file)
@@ -3,27 +3,20 @@
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
 DESCRIPTION
------------
-
-Main Title
 
 Description for main section.
 
-Subsection 1 Title
-
 Description for subsection 1.
 
-Subsection 2 Title
-
 Description for subsection 2.
 
 Description for subsection 3.
 
-Option       Type   Value  Description
----------------------------------------------------
--sub1        int           Option in subsection 1
--sub2        int           Option in subsection 2
--main        int           Option in main section
+OPTIONS
+
+ -sub1  <int>               Option in subsection 1
+ -sub2  <int>               Option in subsection 2
+ -main  <int>               Option in main section
 
 ]]></String>
 </ReferenceData>
index 3a1160286eb5e72ddf4285f9a5e5e71fc4f02e38..69e39dbcbd43eb1516baee99c12f20b110488dc6 100644 (file)
@@ -2,31 +2,25 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
-Option Filename     Type         Description
----------------------------------------------------------
--f     traj.xtc     Input        Input file description
--lib   libdata.dat  Input, Opt., Lib.
-                                 Library file description
--io    inout.dat    In/Out, Opt. Input/Output file description
--o                  Output, Opt. Output file description
+FILE OPTIONS
 
-Option       Type   Value  Description
----------------------------------------------------
--[no]bool    bool   yes    Boolean option
--[no]hidden  bool   yes    Hidden option
--int         int    2      Integer option
--ivec        vector 1 2 3  Integer vector option
--double      double 2.5    Double option
--dvec        vector 1.1 2.3 3.2
-                           Double vector option
--time        time   10     Time option (ps)
--string      string test   String option
--enum        enum   no     Enum option: no, opt1, or opt2
+ -f     <.xtc/.trr/...> (traj.xtc) (Input)
+     Input file description: xtc trr cpt trj gro g96 pdb g87
+ -lib   <.dat>   (libdata.dat) (Input, Opt., Lib.) Library file description
+ -io    <.dat>   (inout.dat) (In/Out, Opt.) Input/Output file description
+ -o     <.xvg>   (Output, Opt.) Output file description
 
-Selection  Description
------------------------------------
--sf        Provide selections from files
--sel       Selection option
+OPTIONS
+
+ -[no]bool       (yes)      Boolean option
+ -[no]hidden     (yes)      Hidden option
+ -int   <int>    (2)        Integer option
+ -ivec  <vector> (1 2 3)    Integer vector option
+ -double <real>  (2.5)      Double option
+ -dvec  <vector> (1.1 2.3 3.2) Double vector option
+ -time  <time>   (10)       Time option (ps)
+ -string <string> (test)    String option
+ -enum  <enum>   (no)       Enum option: no, opt1, opt2
 
 ]]></String>
 </ReferenceData>
diff --git a/src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesSelectionOptions.xml b/src/gromacs/commandline/tests/refdata/CommandLineHelpWriterTest_HandlesSelectionOptions.xml
deleted file mode 100644 (file)
index e15a38c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
-<ReferenceData>
-  <String Name="HelpText"><![CDATA[
-Selection  Description
------------------------------------
--sf        Provide selections from files
--refsel    Reference selection option
-    resname SOL
--sel       Selection option
-    group "Protein" and surface
-    group "Protein" and not surface
-
-]]></String>
-</ReferenceData>
index f5d99a3d2060d4d50593306541ac35a2b8d2689c..88fb03aea558db4d24b6ae1829828cc5648588ca 100644 (file)
@@ -64,11 +64,18 @@ namespace gmx
 class TextTableFormatter::Impl
 {
     public:
+        /*! \internal \brief
+         * Manages a single column for TextTableFormatter.
+         *
+         * \ingroup module_onlinehelp
+         */
         struct ColumnData
         {
+            //! Initializes a text table column with given values.
             ColumnData(const char *title, int width, bool bWrap)
                 : title_(title != NULL ? title : ""),
-                  width_(width), bWrap_(bWrap), firstLine_(0)
+                  width_(width), bWrap_(bWrap), firstLine_(0),
+                  nextLineIndex_(0), nextLineOffset_(0)
             {
                 GMX_ASSERT(width >= 0, "Negative width not possible");
                 GMX_ASSERT(title_.length() <= static_cast<size_t>(width),
@@ -86,34 +93,61 @@ class TextTableFormatter::Impl
              * there is no text.
              */
             int firstLine() const { return firstLine_; }
+
             /*! \brief
-             * Returns the index of the last line with text for the current row.
+             * Resets the formatting state.
              *
-             * If there is no text, returns -1.
+             * After this call, textForNextLine() and hasLinesRemaining() can
+             * be used to format the lines for the column.
              */
-            int lastLine() const
+            void startFormatting()
             {
-                if (lines_.empty())
-                {
-                    return -1;
-                }
-                return firstLine_ + static_cast<int>(lines_.size()) - 1;
+                nextLineIndex_  = (!lines_.empty() ? -firstLine_ : 0);
+                nextLineOffset_ = 0;
+            }
+            //! Whether there are lines remaining for textForNextLine().
+            bool hasLinesRemaining() const
+            {
+                return nextLineIndex_ < static_cast<int>(lines_.size());
             }
             /*! \brief
-             * Returns the text for a line.
+             * Returns the text for the next line.
              *
-             * \param[in] line  Zero-based line index.
-             * \returns   Text for line \p line, or empty string if \p line has
-             *     no text for this column.
+             * \param[in] columnWidth  Width to wrap the text to.
+             * \returns   Text for the next line, or empty string if there is
+             *   no text for this column.
              */
-            std::string textForLine(int line) const
+            std::string textForNextLine(int columnWidth)
             {
-                // The second conditional matches if there are no lines
-                if (line < firstLine() || line > lastLine())
+                if (nextLineIndex_ < 0 || !hasLinesRemaining())
                 {
+                    ++nextLineIndex_;
                     return std::string();
                 }
-                return lines_[line - firstLine()];
+                if (bWrap_)
+                {
+                    TextLineWrapperSettings  settings;
+                    settings.setLineLength(columnWidth);
+                    TextLineWrapper          wrapper(settings);
+                    const std::string       &currentLine = lines_[nextLineIndex_];
+                    const size_t             prevOffset  = nextLineOffset_;
+                    const size_t             nextOffset
+                        = wrapper.findNextLine(currentLine, prevOffset);
+                    if (nextOffset >= currentLine.size())
+                    {
+                        ++nextLineIndex_;
+                        nextLineOffset_ = 0;
+                    }
+                    else
+                    {
+                        nextLineOffset_ = nextOffset;
+                    }
+                    return wrapper.formatLine(currentLine, prevOffset, nextOffset);
+                }
+                else
+                {
+                    return lines_[nextLineIndex_++];
+                }
             }
 
             //! Statit data: title of the column.
@@ -126,6 +160,10 @@ class TextTableFormatter::Impl
             int                         firstLine_;
             //! Text lines for the current row.
             std::vector<std::string>    lines_;
+            //! Formatting state: index in `lines_` for the next line.
+            int                         nextLineIndex_;
+            //! Formatting state: offset within line `nextLineIndex_` for the next line.
+            size_t                      nextLineOffset_;
         };
 
         //! Container type for column data.
@@ -156,6 +194,8 @@ class TextTableFormatter::Impl
         ColumnList              columns_;
         //! Indentation before the first column.
         int                     firstColumnIndent_;
+        //! Indentation before the last column if folded.
+        int                     foldLastColumnToNextLineIndent_;
         //! If true, no output has yet been produced.
         bool                    bFirstRow_;
         //! If true, a header will be printed before the first row.
@@ -163,7 +203,8 @@ class TextTableFormatter::Impl
 };
 
 TextTableFormatter::Impl::Impl()
-    : firstColumnIndent_(0), bFirstRow_(true), bPrintHeader_(false)
+    : firstColumnIndent_(0), foldLastColumnToNextLineIndent_(-1),
+      bFirstRow_(true), bPrintHeader_(false)
 {
 }
 
@@ -195,6 +236,11 @@ void TextTableFormatter::setFirstColumnIndent(int indent)
     impl_->firstColumnIndent_ = indent;
 }
 
+void TextTableFormatter::setFoldLastColumnToNextLine(int indent)
+{
+    impl_->foldLastColumnToNextLineIndent_ = indent;
+}
+
 bool TextTableFormatter::didOutput() const
 {
     return !impl_->bFirstRow_;
@@ -212,12 +258,8 @@ void TextTableFormatter::clear()
 
 void TextTableFormatter::addColumnLine(int index, const std::string &text)
 {
-    Impl::ColumnData &column = impl_->columnData(index);
-    TextLineWrapper   wrapper;
-    if (column.bWrap_)
-    {
-        wrapper.settings().setLineLength(column.width());
-    }
+    Impl::ColumnData        &column = impl_->columnData(index);
+    TextLineWrapper          wrapper;
     std::vector<std::string> lines(wrapper.wrapToVector(text));
     column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
 }
@@ -227,10 +269,9 @@ void TextTableFormatter::addColumnHelpTextBlock(
 {
     Impl::ColumnData       &column = impl_->columnData(index);
     TextLineWrapperSettings settings;
-    if (column.bWrap_)
-    {
-        settings.setLineLength(column.width());
-    }
+    // TODO: If in the future, there is actually a coupling between the markup
+    // and the wrapping, this must be postponed into formatRow(), where we do
+    // the actual line wrapping.
     std::vector<std::string> lines(
             context.substituteMarkupAndWrapToVector(settings, text));
     column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
@@ -243,15 +284,10 @@ void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
     column.firstLine_ = firstLine;
 }
 
-int TextTableFormatter::lastColumnLine(int index) const
-{
-    return impl_->columnData(index).lastLine();
-}
-
 std::string TextTableFormatter::formatRow()
 {
-    std::string result;
-    Impl::ColumnList::const_iterator column;
+    std::string                result;
+    Impl::ColumnList::iterator column;
     // Print a header if this is the first line.
     if (impl_->bPrintHeader_ && impl_->bFirstRow_)
     {
@@ -280,47 +316,89 @@ std::string TextTableFormatter::formatRow()
         result.append("\n");
     }
 
-    // Compute the last applicable line.
-    int lastLine = -1;
+    // Format all the lines, one column at a time.
+    std::vector<std::string> lines;
+    std::vector<std::string> columnLines;
+    int                      currentWidth    = 0;
+    bool                     bFoldLastColumn = false;
     for (column  = impl_->columns_.begin();
          column != impl_->columns_.end();
          ++column)
     {
-        lastLine = std::max(lastLine, column->lastLine());
-    }
-
-    // Format the actual row data.
-    for (int line = 0; line <= lastLine; ++line)
-    {
-        std::string lineResult;
-        size_t      currentWidth = 0;
-        for (column  = impl_->columns_.begin();
-             column != impl_->columns_.end();
-             ++column)
+        // Format the column into columnLines.
+        column->startFormatting();
+        columnLines.clear();
+        columnLines.reserve(lines.size());
+        for (size_t line = 0; column->hasLinesRemaining(); ++line)
         {
-            std::string value(column->textForLine(line));
-            if (column != impl_->columns_.begin())
+            int columnWidth = column->width();
+            if (line < lines.size())
             {
-                ++currentWidth;
-                if (!value.empty())
+                const int overflow = static_cast<int>(lines[line].length()) - currentWidth;
+                if (overflow > 0)
                 {
-                    lineResult.append(" ");
-                    if (lineResult.length() < currentWidth)
+                    if (overflow > columnWidth)
                     {
-                        lineResult.resize(currentWidth, ' ');
+                        columnLines.push_back(std::string());
+                        continue;
                     }
+                    columnWidth -= overflow;
                 }
             }
-            // TODO: Rewrap the text if wrapping is on and the previous columns
-            // overflow.
-            lineResult.append(value);
-            currentWidth += column->width();
+            columnLines.push_back(column->textForNextLine(columnWidth));
         }
+        if (column == impl_->columns_.end() - 1
+            && impl_->foldLastColumnToNextLineIndent_ >= 0
+            && columnLines.size() >= lines.size() + column->lines_.size())
+        {
+            bFoldLastColumn = true;
+            currentWidth   += column->width();
+            break;
+        }
+        // Add columnLines into lines.
+        if (lines.size() < columnLines.size())
+        {
+            lines.resize(columnLines.size());
+        }
+        for (size_t line = 0; line < columnLines.size(); ++line)
+        {
+            if (column != impl_->columns_.begin() && !columnLines[line].empty())
+            {
+                lines[line].append(" ");
+                if (static_cast<int>(lines[line].length()) < currentWidth)
+                {
+                    lines[line].resize(currentWidth, ' ');
+                }
+            }
+            lines[line].append(columnLines[line]);
+        }
+        currentWidth += column->width() + 1;
+    }
+
+    // Construct the result by concatenating all the lines.
+    std::vector<std::string>::const_iterator line;
+    for (line = lines.begin(); line != lines.end(); ++line)
+    {
         result.append(impl_->firstColumnIndent_, ' ');
-        result.append(lineResult);
+        result.append(*line);
         result.append("\n");
     }
 
+    if (bFoldLastColumn)
+    {
+        Impl::ColumnList::reference lastColumn = impl_->columns_.back();
+        const int                   totalIndent
+            = impl_->firstColumnIndent_ + impl_->foldLastColumnToNextLineIndent_;
+        lastColumn.startFormatting();
+        currentWidth -= impl_->foldLastColumnToNextLineIndent_;
+        while (lastColumn.hasLinesRemaining())
+        {
+            result.append(totalIndent, ' ');
+            result.append(lastColumn.textForNextLine(currentWidth));
+            result.append("\n");
+        }
+    }
+
     impl_->bFirstRow_ = false;
     clear();
     return result;
index 46598f97bd4944acd104de8b0540a730b8cd364f..6891adfb0845bdd56c32cb4822110f2b4d673146 100644 (file)
@@ -128,6 +128,23 @@ class TextTableFormatter
          * Does not throw.
          */
         void setFirstColumnIndent(int indent);
+        /*! \brief
+         * Enables folding the last column to separate lines if it overflows.
+         *
+         * \param[in]  indent  Number of spaces to use for indenting the lines.
+         *
+         * If called with `indent >= 0`, the last column for each row is
+         * treated specially: if it contains more lines than the other columns,
+         * and if the text would fit more compactly as separate lines after the
+         * row, then the whole last column is written after the row with the
+         * given \p indent.  The column text then spans the whole space
+         * reserved for the table, making long text fit into a smaller amount
+         * of vertical space.
+         * If not called, the last column is not treates specially.
+         *
+         * Does not throw.
+         */
+        void setFoldLastColumnToNextLine(int indent);
 
         /*! \brief
          * Whether formatRow() has been successfully called.
@@ -209,20 +226,6 @@ class TextTableFormatter
          */
         std::string formatRow();
 
-        /*! \brief
-         * Returns the last line on which column \p index has text.
-         *
-         * \param[in] index  Zero-based column index.
-         * \returns   Last line index (zero-based) on which \p index has text.
-         *
-         * The return value is the sum of the number of lines added with
-         * addColumnLine() (taking into account possible wrapping) and the line
-         * offset set with setColumnFirstLineOffset().
-         *
-         * Does not throw.
-         */
-        int lastColumnLine(int index) const;
-
     private:
         class Impl;
 
index 42517ed38ec58024017edfdfb7bdd8b53ae501c5..7bfc31e2bd2863a480ffe00f3b179d42bb2208b0 100644 (file)
@@ -49,7 +49,6 @@
 
 #include <boost/shared_ptr.hpp>
 
-#include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
@@ -649,16 +648,23 @@ HelpWriterContext::substituteMarkupAndWrapToVector(
 
 void HelpWriterContext::writeTitle(const std::string &title) const
 {
-    if (outputFormat() != eHelpOutputFormat_Console)
+    File &file = outputFile();
+    switch (outputFormat())
     {
-        // TODO: Implement once the situation with Redmine issue #969 is more
-        // clear.
-        GMX_THROW(NotImplementedError(
-                          "This output format is not implemented"));
+        case eHelpOutputFormat_Console:
+            file.writeLine(toUpperCase(title));
+            file.writeLine();
+            break;
+        case eHelpOutputFormat_Man:
+            file.writeLine(formatString(".SH %s", toUpperCase(title).c_str()));
+            break;
+        case eHelpOutputFormat_Html:
+            file.writeLine(formatString("<H3>%s</H3>", title.c_str()));
+            break;
+        default:
+            GMX_THROW(NotImplementedError(
+                              "This output format is not implemented"));
     }
-    File &file = outputFile();
-    file.writeLine(toUpperCase(title));
-    file.writeLine();
 }
 
 void HelpWriterContext::writeTextBlock(const std::string &text) const
@@ -671,4 +677,55 @@ void HelpWriterContext::writeTextBlock(const std::string &text) const
     outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
 }
 
+void HelpWriterContext::writeOptionListStart() const
+{
+    if (outputFormat() == eHelpOutputFormat_Html)
+    {
+        outputFile().writeLine("<dl>");
+    }
+}
+
+void HelpWriterContext::writeOptionItem(const std::string &name,
+                                        const std::string &args,
+                                        const std::string &description) const
+{
+    File &file = outputFile();
+    switch (outputFormat())
+    {
+        case eHelpOutputFormat_Console:
+            // TODO: Generalize this when there is need for it; the current,
+            // special implementation is in CommandLineHelpWriter.
+            GMX_THROW(NotImplementedError("Option item formatting for console output not implemented"));
+            break;
+        case eHelpOutputFormat_Man:
+            file.writeLine(formatString(".BI \"\\%s\" \" %s\"", name.c_str(), args.c_str()));
+            file.writeString("    ");
+            writeTextBlock(description);
+            file.writeLine();
+            break;
+        case eHelpOutputFormat_Html:
+        {
+            std::string substArgs =
+                substituteMarkupAndWrapToString(TextLineWrapperSettings(), args);
+            file.writeLine(formatString("<dt><b><tt>%s</tt></b> %s</dt>", name.c_str(),
+                                        substArgs.c_str()));
+            file.writeLine("<dd>");
+            writeTextBlock(description);
+            file.writeLine("</dd>");
+            break;
+        }
+        default:
+            GMX_THROW(NotImplementedError(
+                              "This output format is not implemented"));
+    }
+}
+
+void HelpWriterContext::writeOptionListEnd() const
+{
+    if (outputFormat() == eHelpOutputFormat_Html)
+    {
+        outputFile().writeLine("</dl>");
+    }
+}
+
 } // namespace gmx
index c0947005e2635c7e94a63e94b8b4f377fc1c3172..026392697c68627d72c9d6bc723ad973403103f9 100644 (file)
@@ -232,6 +232,30 @@ class HelpWriterContext
          * and writes the result directly to the output file.
          */
         void writeTextBlock(const std::string &text) const;
+        /*! \brief
+         * Starts writing a list of options.
+         *
+         * Prints any necessary headers for a list of options formatted with
+         * writeOptionItem().
+         */
+        void writeOptionListStart() const;
+        /*! \brief
+         * Writes an entry for a single option into the output.
+         *
+         * \param[in] name  Name of the option.
+         * \param[in] args  Placeholder for values and other information about
+         *     the option (placed after \p name).
+         * \param[in] description  Full description of the option.
+         */
+        void writeOptionItem(const std::string &name, const std::string &args,
+                             const std::string &description) const;
+        /*! \brief
+         * Finishes writing a list of options.
+         *
+         * Prints any necessary footers for a list of options formatted with
+         * writeOptionItem().
+         */
+        void writeOptionListEnd() const;
 
     private:
         class Impl;
index 58af561faab5abc5afe7aa365502f8e28355836e..74a77b7aa780334fd1b4d73acf3880a11ef4aed0 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013, 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.
@@ -137,6 +137,30 @@ TEST_F(TextTableFormatterTest, HandlesOverflowingLines)
     checkText(formatter_.formatRow(), "FormattedRow3");
 }
 
+TEST_F(TextTableFormatterTest, HandlesLastColumnFolding)
+{
+    setupStandardColumns();
+    formatter_.setFoldLastColumnToNextLine(4);
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(2, "text");
+    formatter_.addColumnLine(3, g_wrapText);
+    checkText(formatter_.formatRow(), "FormattedTable");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(2, "text");
+    formatter_.addColumnLine(3, g_wrapText2);
+    checkText(formatter_.formatRow(), "FormattedRow2");
+    formatter_.clear();
+    formatter_.addColumnLine(0, "foo");
+    formatter_.addColumnLine(1, "bar");
+    formatter_.addColumnLine(2, "text");
+    formatter_.addColumnLine(3, "A quick brown fox jumps");
+    checkText(formatter_.formatRow(), "FormattedRow3");
+}
+
 TEST_F(TextTableFormatterTest, HandlesEmptyColumns)
 {
     setupStandardColumns();
diff --git a/src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesLastColumnFolding.xml b/src/gromacs/onlinehelp/tests/refdata/TextTableFormatterTest_HandlesLastColumnFolding.xml
new file mode 100644 (file)
index 0000000..8e4c940
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="FormattedTable"><![CDATA[
+Col1 Col2 Col3Wrap       Col4Wrap
+---------------------------------------
+foo  bar  text
+    A quick brown fox jumps over the
+    lazy dog
+]]></String>
+  <String Name="FormattedRow2"><![CDATA[
+foo  bar  text
+    A quick brown fox jumps
+    over the lazy dog
+]]></String>
+  <String Name="FormattedRow3"><![CDATA[
+foo  bar  text
+    A quick brown fox jumps
+]]></String>
+</ReferenceData>
index 69ef2c69e88b26ccebea9b1ca18d29409130fc86..d4a44b43f56ba241c3d8f811e9f2316c52a35b02 100644 (file)
@@ -4,16 +4,16 @@
   <String Name="FormattedTable"><![CDATA[
 Col1 Col2 Col3Wrap       Col4Wrap
 ---------------------------------------
-foobar barfoo A quick brown A quick brown
-          fox jumps over fox jumps
-          the lazy dog   over the lazy
-                         dog
+foobar barfoo A quick    A quick brown
+          brown fox      fox jumps
+          jumps over the over the lazy
+          lazy dog       dog
 ]]></String>
   <String Name="FormattedRow2"><![CDATA[
 foobar    A quick brown  A quick brown
-     barfoo fox jumps over fox jumps
-          the lazy dog   over the lazy
-                         dog
+     barfoo fox jumps    fox jumps
+          over the lazy  over the lazy
+          dog            dog
 ]]></String>
   <String Name="FormattedRow3"><![CDATA[
 foobar                   A quick brown
index 6be36435af10c98b5826e3fec420932c2a241b59..d0623ad69d0aeea8e89d0d93d88939f88681bc11 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -214,14 +214,20 @@ const std::string &OptionInfo::name() const
     return option().name();
 }
 
-const std::string &OptionInfo::description() const
+std::string OptionInfo::type() const
 {
-    return option().description();
+    return option().typeString();
 }
 
-const char *OptionInfo::type() const
+std::string OptionInfo::formatDescription() const
 {
-    return option().typeString();
+    std::string description(option().description());
+    std::string extraDescription(option().formatExtraDescription());
+    if (!extraDescription.empty())
+    {
+        description.append(extraDescription);
+    }
+    return description;
 }
 
 int OptionInfo::valueCount() const
index 0bfd9b74668b738186b2f701a22f77ff9a099451..8a9837b5b441745320f127602b7d731172bdba24 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -443,10 +443,10 @@ class OptionInfo
         bool isRequired() const;
         //! Returns the name of the option.
         const std::string &name() const;
-        //! Returns the description of the option.
-        const std::string &description() const;
         //! Returns the type of the option as a string.
-        const char *type() const;
+        std::string type() const;
+        //! Returns the description of the option.
+        std::string formatDescription() const;
         //! Returns the number of values given for the option.
         int valueCount() const;
         //! Returns the i'th value of the option as a string.
index 9a7a81147be55659e88c85bd9f6a81bea5fa5233..791c4edea06f63f65049cf5dc3ae9385fca4b51f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2014, 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.
@@ -106,7 +106,7 @@ class AbstractOptionStorage
         bool isVector() const { return hasFlag(efOption_Vector); }
         //! Returns the name of the option.
         const std::string &name() const { return name_; }
-        //! Returns the description of the option.
+        //! Returns the description of the option set by the calling code.
         const std::string &description() const { return descr_; }
 
         /*! \brief
@@ -115,10 +115,17 @@ class AbstractOptionStorage
         virtual OptionInfo &optionInfo() = 0;
         /*! \brief
          * Returns a short string describing the type of the option.
+         */
+        virtual std::string typeString() const = 0;
+        /*! \brief
+         * Formats additional description for the option.
          *
-         * The caller is free to discard the returned string.
+         * If this method returns a non-empty string, it is appended to the
+         * plain description when printing help texts.
+         * The default implementation returns an empty string.
          */
-        virtual const char *typeString() const = 0;
+        virtual std::string formatExtraDescription() const
+        { return std::string(); }
         /*! \brief
          * Returns the number of option values added so far.
          */
index a7ee949f248279b8a86c6ac0f95fb11d17cb0db5..a7611e740ba5271cabcbf60debff28c49e48ccf2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -248,9 +248,9 @@ DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
 {
 }
 
-const char *DoubleOptionStorage::typeString() const
+std::string DoubleOptionStorage::typeString() const
 {
-    return isVector() ? "vector" : (isTime() ? "time" : "double");
+    return isVector() ? "vector" : (isTime() ? "time" : "real");
 }
 
 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
@@ -349,7 +349,7 @@ FloatOptionStorage::FloatOptionStorage(const FloatOption &settings)
 {
 }
 
-const char *FloatOptionStorage::typeString() const
+std::string FloatOptionStorage::typeString() const
 {
     return isVector() ? "vector" : (isTime() ? "time" : "real");
 }
@@ -520,6 +520,25 @@ StringOptionStorage::StringOptionStorage(const StringOption &settings)
     }
 }
 
+std::string StringOptionStorage::formatExtraDescription() const
+{
+    std::string result;
+    if (!allowed_.empty())
+    {
+        result.append(": ");
+        ValueList::const_iterator i;
+        for (i = allowed_.begin(); i != allowed_.end(); ++i)
+        {
+            if (i != allowed_.begin())
+            {
+                result.append(", ");
+            }
+            result.append(*i);
+        }
+    }
+    return result;
+}
+
 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
 {
     return value;
index 509578cda8872746eef59bdaa59d32d35a9134d9..2d674335f3b7db9f792c87ba1327eb959e2a947c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -72,7 +72,7 @@ class BooleanOptionStorage : public OptionStorageTemplate<bool>
         }
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const { return "bool"; }
+        virtual std::string typeString() const { return "bool"; }
         virtual std::string formatSingleValue(const bool &value) const;
 
     private:
@@ -94,7 +94,7 @@ class IntegerOptionStorage : public OptionStorageTemplate<int>
         }
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const
+        virtual std::string typeString() const
         { return isVector() ? "vector" : "int"; }
         virtual std::string formatSingleValue(const int &value) const;
 
@@ -118,8 +118,7 @@ class Int64OptionStorage : public OptionStorageTemplate<gmx_int64_t>
         }
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const
-        { return "int"; }
+        virtual std::string typeString() const { return "int"; }
         virtual std::string formatSingleValue(const gmx_int64_t &value) const;
 
     private:
@@ -138,7 +137,7 @@ class DoubleOptionStorage : public OptionStorageTemplate<double>
         explicit DoubleOptionStorage(const DoubleOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const;
+        virtual std::string typeString() const;
         virtual std::string formatSingleValue(const double &value) const;
 
         //! \copydoc DoubleOptionInfo::isTime()
@@ -165,7 +164,7 @@ class FloatOptionStorage : public OptionStorageTemplate<float>
         explicit FloatOptionStorage(const FloatOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const;
+        virtual std::string typeString() const;
         virtual std::string formatSingleValue(const float &value) const;
 
         //! \copydoc DoubleOptionStorage::isTime()
@@ -192,7 +191,9 @@ class StringOptionStorage : public OptionStorageTemplate<std::string>
         explicit StringOptionStorage(const StringOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const { return allowed_.empty() ? "string" : "enum"; }
+        virtual std::string typeString() const
+        { return allowed_.empty() ? "string" : "enum"; }
+        virtual std::string formatExtraDescription() const;
         virtual std::string formatSingleValue(const std::string &value) const;
 
         //! \copydoc StringOptionInfo::allowedValues()
index 5791585e9b016983977476bc23c5b0758f16aeb1..ae40a3a6cfbe3ebdbda1e1c32fd930ca79ed0065 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014, 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.
@@ -58,18 +58,25 @@ namespace
 
 class FileTypeRegistry;
 
+//! \addtogroup module_options
+//! \{
+
+//! Shorthand for a list of file extensions.
+typedef std::vector<const char *> ExtensionList;
+
 /********************************************************************
  * FileTypeHandler
  */
 
 /*! \internal \brief
  * Handles a single file type known to FileNameOptionStorage.
- *
- * \ingroup module_options
  */
 class FileTypeHandler
 {
     public:
+        //! Returns the list of extensions for this file type.
+        const ExtensionList &extensions() const { return extensions_; }
+
         //! Returns whether \p filename has a valid extension for this type.
         bool hasKnownExtension(const std::string &filename) const;
         //! Adds a default extension for this type to \p filename.
@@ -86,7 +93,7 @@ class FileTypeHandler
 
     private:
         //! Possible extensions for this file type.
-        std::vector<const char *> extensions_;
+        ExtensionList extensions_;
 
         /*! \brief
          * Needed for initialization; all initialization is handled by
@@ -138,8 +145,6 @@ FileTypeHandler::findFileWithExtension(const std::string &filename) const
 
 /*! \internal \brief
  * Singleton for managing static file type info for FileNameOptionStorage.
- *
- * \ingroup module_options
  */
 class FileTypeRegistry
 {
@@ -253,6 +258,8 @@ std::string completeFileName(const std::string &value, OptionFileType filetype,
     return typeHandler.addExtension(value);
 }
 
+//! \}
+
 }   // namespace
 
 /********************************************************************
@@ -279,6 +286,52 @@ FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
     }
 }
 
+std::string FileNameOptionStorage::typeString() const
+{
+    const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
+    const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_);
+    const ExtensionList          &extensions  = typeHandler.extensions();
+    std::string                   result;
+    ExtensionList::const_iterator i;
+    int                           count = 0;
+    for (i = extensions.begin(); count < 2 && i != extensions.end(); ++i, ++count)
+    {
+        if (i != extensions.begin())
+        {
+            result.append("/");
+        }
+        result.append(*i);
+    }
+    if (i != extensions.end())
+    {
+        result.append("/...");
+    }
+    if (result.empty())
+    {
+        result = "file";
+    }
+    return result;
+}
+
+std::string FileNameOptionStorage::formatExtraDescription() const
+{
+    const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
+    const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_);
+    const ExtensionList          &extensions  = typeHandler.extensions();
+    std::string                   result;
+    if (extensions.size() > 2)
+    {
+        result.append(":");
+        ExtensionList::const_iterator i;
+        for (i = extensions.begin(); i != extensions.end(); ++i)
+        {
+            result.append(" ");
+            result.append((*i) + 1);
+        }
+    }
+    return result;
+}
+
 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
 {
     return value;
index c3d0ae3d321518f9352e52a4f72a20a94b1ba2ba..3a8ac03311148c21b60816dd6b15dda3b46f151d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2014, 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.
@@ -63,7 +63,8 @@ class FileNameOptionStorage : public OptionStorageTemplate<std::string>
         explicit FileNameOptionStorage(const FileNameOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const { return "file"; }
+        virtual std::string typeString() const;
+        virtual std::string formatExtraDescription() const;
         virtual std::string formatSingleValue(const std::string &value) const;
 
         //! \copydoc FileNameOptionInfo::isInputFile()
index 2462f1babfad632aa58e10099d98af2f8fae742b..3085a4191092bd6c0d39d11af50035628dcf09e6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -97,7 +97,7 @@ class OptionStorageTemplate : public AbstractOptionStorage
 
         // No implementation in this class for the pure virtual methods, but
         // the declarations are still included for clarity.
-        virtual const char *typeString() const = 0;
+        virtual std::string typeString() const = 0;
         virtual int valueCount() const { return static_cast<int>(values_->size()); }
         /*! \copydoc gmx::AbstractOptionStorage::formatValue()
          *
index cf76386af2b6e62e558db6c2cbc68c7a13493da7..716e0b1f56de278c8b101c4d6015b84ad2a7f437 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
+ * Copyright (c) 2010,2011,2012,2013,2014, 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.
@@ -119,7 +119,7 @@ class MockOptionStorage : public gmx::OptionStorageTemplate<std::string>
 
         virtual gmx::OptionInfo &optionInfo() { return info_; }
         // These are not used.
-        virtual const char *typeString() const { return "mock"; }
+        virtual std::string typeString() const { return "mock"; }
         virtual std::string formatSingleValue(const std::string & /*value*/) const
         {
             return "";
index 77e3d324bd9a1ac1b9a3cb58923a56c6b08357e9..a19321962fe2d2314b490cec50c456b721da8f16 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013,2014, 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.
@@ -67,7 +67,7 @@ class SelectionFileOptionStorage : public AbstractOptionStorage
         SelectionFileOptionStorage(const SelectionFileOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const { return "file"; }
+        virtual std::string typeString() const { return "file"; }
         virtual int valueCount() const { return 0; }
         virtual std::string formatValue(int /*i*/) const { return ""; }
 
index 2b5f645f35721ecd256efec524efd7f56cf00ee0..00e3622ba82717d2811090dc789c7e9703308524 100644 (file)
@@ -73,7 +73,7 @@ class SelectionOptionStorage : public OptionStorageTemplate<Selection>
         SelectionOptionStorage(const SelectionOption &settings);
 
         virtual OptionInfo &optionInfo() { return info_; }
-        virtual const char *typeString() const { return "sel"; }
+        virtual std::string typeString() const { return "selection"; }
         virtual std::string formatSingleValue(const Selection &value) const;
 
         //! \copydoc SelectionOptionInfo::setManager()
index 7e0dfe854891c69893acc0c334003df9b7f09c93..43f59ad33a0178449134b3d388c80cdb7835be83 100644 (file)
@@ -330,11 +330,6 @@ int TrajectoryAnalysisCommandLineRunner::Impl::RunnerCommandLineModule::run(
 void TrajectoryAnalysisCommandLineRunner::Impl::RunnerCommandLineModule::writeHelp(
         const CommandLineHelpContext &context) const
 {
-    // TODO: Implement #969.
-    if (context.writerContext().outputFormat() != eHelpOutputFormat_Console)
-    {
-        return;
-    }
     TrajectoryAnalysisModulePointer     module(factory_());
     TrajectoryAnalysisCommandLineRunner runner(module.get());
     runner.writeHelp(context);
index 1c9eacc5efb0816c0317f979cf44db782b84a084..9f9e23e0a4a2acdd23b054823e0541941fd82142 100644 (file)
@@ -354,7 +354,7 @@ Angle::initOptions(Options *options, TrajectoryAnalysisSettings * /*settings*/)
         "With [TT]-g2 t0[tt], [TT]-group2[tt] is not necessary, and angles",
         "are calculated from the vectors as they are in the first frame.[PAR]",
         "There are three options for output:",
-        "[TT]-oav[tt] writes an xvgr file with the time and the average angle",
+        "[TT]-oav[tt] writes an xvg file with the time and the average angle",
         "for each frame.",
         "[TT]-oall[tt] writes all the individual angles.",
         "[TT]-oh[tt] writes a histogram of the angles. The bin width can be",
index ead785a31803f2251ce4e49b13c93a95f818ac1d..54d933169ab18da77e84061fc697edff09713f4d 100644 (file)
@@ -163,7 +163,7 @@ FreeVolume::initOptions(Options                    *options,
                         TrajectoryAnalysisSettings *settings)
 {
     static const char *const desc[] = {
-        "[THISMODULE] can calculate the free volume in a box as",
+        "[THISMODULE] calculates the free volume in a box as",
         "a function of time. The free volume is",
         "plotted as a fraction of the total volume.",
         "The program tries to insert a probe with a given radius,",
index 2b0fc38fe684831b0b0ae1d2d20223cc37e3784c..c734b8fbb317b5d9e5c47ce0cf30ee797756cdb3 100644 (file)
@@ -186,7 +186,6 @@ TrajectoryAnalysisRunnerCommon::initOptions(Options *options)
                            .store(&impl_->ndxfile_)
                            .defaultBasename("index")
                            .description("Extra index groups"));
-    options->addOption(SelectionFileOption("sf"));
 
     // Add options for trajectory time control.
     options->addOption(DoubleOption("b").store(&impl_->startTime_).timeValue()
@@ -213,6 +212,8 @@ TrajectoryAnalysisRunnerCommon::initOptions(Options *options)
         options->addOption(BooleanOption("pbc").store(&settings.impl_->bPBC)
                                .description("Use periodic boundary conditions for distance calculation"));
     }
+
+    options->addOption(SelectionFileOption("sf"));
 }