Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpwriter.cpp
index 029aef5b44cbfe3e5bede231f3935ddeb8cde47b..0c79bd393451006b0e4c5161ced2b0ab4170a1dd 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2010,2011,2012,2013, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * 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.
  *
  * GROMACS is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
  * \author Teemu Murtola <teemu.murtola@gmail.com>
  * \ingroup module_commandline
  */
+#include "gmxpre.h"
+
 #include "cmdlinehelpwriter.h"
 
+#include <cstring>
+
+#include <algorithm>
 #include <string>
 
+#include <boost/scoped_ptr.hpp>
+
 #include "gromacs/commandline/cmdlinehelpcontext.h"
 #include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
 #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/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "shellcompletions.h"
+
 namespace gmx
 {
 
 namespace
 {
 
+//! \addtogroup module_commandline
+//! \{
+
+/********************************************************************
+ * DescriptionsFormatter
+ */
+
+class DescriptionsFormatter : public OptionsVisitor
+{
+    public:
+        /*! \brief
+         * Creates a new description formatter.
+         *
+         * \param[in] context   Help context to use to write the help.
+         */
+        explicit DescriptionsFormatter(const HelpWriterContext &context)
+            : context_(context), title_(NULL), bDidOutput_(false)
+        {
+        }
+
+        //! Formats all section descriptions from a given options.
+        void format(const Options &options, const char *title)
+        {
+            title_      = title;
+            bDidOutput_ = false;
+            visitSubSection(options);
+            if (bDidOutput_)
+            {
+                context_.outputFile().writeLine();
+            }
+        }
+
+        //! Formats the description for a single subsection and handles recursion.
+        virtual void visitSubSection(const Options &section);
+        // This method is not used and never called.
+        virtual void visitOption(const OptionInfo & /*option*/) {}
+
+    private:
+        const HelpWriterContext &context_;
+        const char              *title_;
+        bool                     bDidOutput_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(DescriptionsFormatter);
+};
+
+void DescriptionsFormatter::visitSubSection(const Options &section)
+{
+    if (!section.description().empty())
+    {
+        if (bDidOutput_)
+        {
+            context_.outputFile().writeLine();
+        }
+        else if (title_ != NULL)
+        {
+            context_.writeTitle(title_);
+        }
+        // TODO: Print title for the section?
+        context_.writeTextBlock(section.description());
+        bDidOutput_ = true;
+    }
+
+    OptionsIterator iterator(section);
+    iterator.acceptSubSections(this);
+}
+
 /********************************************************************
  * OptionsFormatterInterface
  */
@@ -71,26 +145,14 @@ namespace
  * Interface for output format specific formatting of options.
  *
  * \see OptionsFilter
- *
- * \ingroup module_commandline
  */
 class OptionsFormatterInterface
 {
     public:
         virtual ~OptionsFormatterInterface() {}
 
-        //! Formats the description text block for a section.
-        virtual void formatDescription(const HelpWriterContext &context,
-                                       const Options           &section) = 0;
-        //! Formats a single file option.
-        virtual void formatFileOption(const HelpWriterContext  &context,
-                                      const FileNameOptionInfo &option) = 0;
-        //! 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;
+        //! Formats a single option option.
+        virtual void formatOption(const OptionInfo &option) = 0;
 };
 
 /********************************************************************
@@ -105,8 +167,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
 {
@@ -114,25 +174,18 @@ class OptionsFilter : public OptionsVisitor
         //! Specifies the type of output that formatSelected() produces.
         enum FilterType
         {
-            eSelectDescriptions,
             eSelectFileOptions,
-            eSelectSelectionOptions,
             eSelectOtherOptions
         };
 
         /*! \brief
          * Creates a new filtering object.
          *
-         * \param[in] context   Help context to use to write the help.
-         * \param[in] formatter Output format specific formatter.
-         *
          * Does not throw.
          */
-        OptionsFilter(const HelpWriterContext   &context,
-                      OptionsFormatterInterface *formatter)
-            : context_(context), formatter_(*formatter),
-              filterType_(eSelectOtherOptions), bShowHidden_(false),
-              bDidOutput_(false)
+        OptionsFilter()
+            : formatter_(NULL), filterType_(eSelectOtherOptions),
+              bShowHidden_(false)
         {
         }
 
@@ -143,42 +196,32 @@ class OptionsFilter : public OptionsVisitor
         }
 
         //! Formats selected options using the formatter.
-        void formatSelected(FilterType type, const Options &options);
+        void formatSelected(FilterType                 type,
+                            OptionsFormatterInterface *formatter,
+                            const Options             &options);
 
         virtual void visitSubSection(const Options &section);
         virtual void visitOption(const OptionInfo &option);
 
     private:
-        const HelpWriterContext        &context_;
-        OptionsFormatterInterface      &formatter_;
+        OptionsFormatterInterface      *formatter_;
         FilterType                      filterType_;
         bool                            bShowHidden_;
-        bool                            bDidOutput_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
 };
 
-void OptionsFilter::formatSelected(FilterType type, const Options &options)
+void OptionsFilter::formatSelected(FilterType                 type,
+                                   OptionsFormatterInterface *formatter,
+                                   const Options             &options)
 {
+    formatter_  = formatter;
     filterType_ = type;
-    bDidOutput_ = false;
     visitSubSection(options);
-    if (bDidOutput_)
-    {
-        context_.outputFile().writeLine();
-    }
 }
 
 void OptionsFilter::visitSubSection(const Options &section)
 {
-    if (filterType_ == eSelectDescriptions)
-    {
-        if (bDidOutput_)
-        {
-            context_.outputFile().writeLine();
-        }
-        formatter_.formatDescription(context_, section);
-        bDidOutput_ = true;
-    }
-
     OptionsIterator iterator(section);
     iterator.acceptSubSections(this);
     iterator.acceptOptions(this);
@@ -194,26 +237,13 @@ void OptionsFilter::visitOption(const OptionInfo &option)
     {
         if (filterType_ == eSelectFileOptions)
         {
-            formatter_.formatFileOption(context_,
-                                        *option.toType<FileNameOptionInfo>());
-            bDidOutput_ = true;
-        }
-        return;
-    }
-    if (option.isType<SelectionFileOptionInfo>()
-        || option.isType<SelectionOptionInfo>())
-    {
-        if (filterType_ == eSelectSelectionOptions)
-        {
-            formatter_.formatSelectionOption(context_, option);
-            bDidOutput_ = true;
+            formatter_->formatOption(option);
         }
         return;
     }
     if (filterType_ == eSelectOtherOptions)
     {
-        formatter_.formatOption(context_, option);
-        bDidOutput_ = true;
+        formatter_->formatOption(option);
         return;
     }
 }
@@ -234,110 +264,68 @@ 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 option name and value.
+void formatOptionNameAndValue(const OptionInfo &option, std::string *name,
+                              std::string *value)
 {
-    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())
+    *name  = option.name();
+    *value = "<" + option.type() + ">";
+    if (option.isType<FileNameOptionInfo>())
     {
-        File              &file  = context.outputFile();
-        const std::string &title = section.title();
-        if (!title.empty())
+        // TODO: Make these work also for other option types.
+        if (option.maxValueCount() != 1)
+        {
+            *value += " [...]";
+        }
+        if (option.minValueCount() == 0)
         {
-            file.writeLine(title);
-            file.writeLine();
+            *value = "[" + *value + "]";
         }
-        context.writeTextBlock(section.description());
+    }
+    if (option.isType<BooleanOptionInfo>())
+    {
+        *name = "[no]" + *name;
+        // Old command-line parser doesn't accept any values for these.
+        // value = "[" + value + "]";
+        value->clear();
     }
 }
 
-void OptionsConsoleFormatter::formatFileOption(
-        const HelpWriterContext &context, const FileNameOptionInfo &option)
+//! Formats the default option value as a string.
+std::string
+defaultOptionValue(const OptionInfo &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)
+    if (option.valueCount() == 0
+        || (option.valueCount() == 1 && option.formatValue(0).empty()))
     {
-        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)
+        return option.formatDefaultValueIfSet();
+    }
+    else
+    {
+        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,126 +337,261 @@ 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_.addColumnLine(3, context.substituteMarkup(option.description()));
+    return type;
+}
+
+//! Formats the description for an option as a string.
+std::string
+descriptionWithOptionDetails(const CommonFormatterData &common,
+                             const OptionInfo          &option)
+{
+    std::string             description(option.formatDescription());
 
-    // Compute layout.
-    if (name.length() > 6U || firstShortValue > 0)
+    const FloatOptionInfo  *floatOption  = option.toType<FloatOptionInfo>();
+    const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
+    if ((floatOption != NULL && floatOption->isTime())
+        || (doubleOption != NULL && doubleOption->isTime()))
     {
-        fileOptionFormatter_.setColumnFirstLineOffset(1, 1);
-        // Assume that the name is <20 chars, so that the type fits
-        if (firstLongValue >= 0)
+        // TODO: It could be nicer to have this in basicoptions.cpp.
+        description = replaceAll(description, "%t", common.timeUnit);
+    }
+
+    return description;
+}
+
+/********************************************************************
+ * OptionsSynopsisFormatter
+ */
+
+/*! \brief
+ * Formatter implementation for synopsis.
+ */
+class SynopsisFormatter : public OptionsFormatterInterface
+{
+    public:
+        //! Creates a helper object for formatting the synopsis.
+        explicit SynopsisFormatter(const HelpWriterContext &context)
+            : context_(context), lineLength_(0), indent_(0), currentLength_(0)
         {
-            ++firstLongValue;
-            ++lastLongValue;
         }
-    }
-    int firstDescriptionLine = 0;
-    if (bLongType)
+
+        //! Starts formatting the synopsis.
+        void start(const char *name);
+        //! Finishes formatting the synopsis.
+        void finish();
+
+        virtual void formatOption(const OptionInfo &option);
+
+    private:
+        const HelpWriterContext &context_;
+        int                      lineLength_;
+        int                      indent_;
+        int                      currentLength_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(SynopsisFormatter);
+};
+
+void SynopsisFormatter::start(const char *name)
+{
+    currentLength_ = std::strlen(name) + 1;
+    indent_        = std::min(currentLength_, 13);
+    File &file = context_.outputFile();
+    switch (context_.outputFormat())
     {
-        firstDescriptionLine = 1;
+        case eHelpOutputFormat_Console:
+            lineLength_ = 78;
+            file.writeString(name);
+            break;
+        case eHelpOutputFormat_Man:
+            lineLength_ = 70;
+            file.writeString(name);
+            break;
+        case eHelpOutputFormat_Html:
+            lineLength_ = 78;
+            file.writeLine("<pre>");
+            file.writeString(name);
+            break;
+        default:
+            GMX_THROW(NotImplementedError("Synopsis formatting not implemented for this output format"));
     }
-    fileOptionFormatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
-    if (firstLongValue >= 0
-        && fileOptionFormatter_.lastColumnLine(3) >= firstLongValue)
+}
+
+void SynopsisFormatter::finish()
+{
+    File &file = context_.outputFile();
+    file.writeLine();
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
     {
-        firstDescriptionLine = lastLongValue + 1;
-        fileOptionFormatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
+        file.writeLine("</pre>");
     }
-
-    // Do the formatting.
-    context.outputFile().writeString(fileOptionFormatter_.formatRow());
+    file.writeLine();
 }
 
-void OptionsConsoleFormatter::formatOption(
-        const HelpWriterContext &context, const OptionInfo &option)
+void SynopsisFormatter::formatOption(const OptionInfo &option)
 {
-    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)
+    std::string name, value;
+    formatOptionNameAndValue(option, &name, &value);
+    std::string fullOptionText(" [-" + name);
+    if (!value.empty())
     {
-        genericOptionFormatter_.setColumnFirstLineOffset(1, 1);
+        fullOptionText.append(" ");
+        fullOptionText.append(value);
     }
+    fullOptionText.append("]");
+    const int   totalLength = fullOptionText.size();
 
-    // TODO: Better handling of multiple long values
-    std::string values;
-    for (int i = 0; i < option.valueCount(); ++i)
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
     {
-        if (i != 0)
-        {
-            values.append(" ");
-        }
-        values.append(option.formatValue(i));
+        value = replaceAll(value, "<", "&lt;");
+        value = replaceAll(value, ">", "&gt;");
     }
-    genericOptionFormatter_.addColumnLine(2, values);
 
-    std::string             description(context.substituteMarkup(option.description()));
-    const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
-    if (doubleOption != NULL && doubleOption->isTime())
+    File &file = context_.outputFile();
+    currentLength_ += totalLength;
+    if (currentLength_ >= lineLength_)
     {
-        description = replaceAll(description, "%t", common_.timeUnit);
+        file.writeString(formatString("\n%*c", indent_ - 1, ' '));
+        currentLength_ = indent_ - 1 + totalLength;
     }
-    const StringOptionInfo *stringOption = option.toType<StringOptionInfo>();
-    if (stringOption != NULL && stringOption->isEnumerated())
-    {
-        const std::vector<std::string> &allowedValues
-            = stringOption->allowedValues();
-        description.append(": ");
-        for (size_t i = 0; i < allowedValues.size(); ++i)
+    file.writeString(fullOptionText);
+}
+
+/********************************************************************
+ * OptionsListFormatter
+ */
+
+/*! \brief
+ * Formatter implementation for help export.
+ */
+class OptionsListFormatter : public OptionsFormatterInterface
+{
+    public:
+        //! Creates a helper object for formatting options.
+        OptionsListFormatter(const HelpWriterContext   &context,
+                             const CommonFormatterData &common,
+                             const char                *title);
+
+        //! Initiates a new section in the options list.
+        void startSection(const char *header)
+        {
+            header_     = header;
+            bDidOutput_ = false;
+        }
+        //! Finishes a section in the options list.
+        void finishSection()
         {
-            if (i > 0)
+            if (bDidOutput_)
             {
-                description.append(i + 1 < allowedValues.size()
-                                   ? ", " : ", or ");
+                context_.writeOptionListEnd();
+                context_.outputFile().writeLine();
             }
-            description.append(allowedValues[i]);
         }
-    }
-    genericOptionFormatter_.addColumnLine(3, description);
-    if (values.length() > 6U)
+
+        virtual void formatOption(const OptionInfo &option);
+
+    private:
+        void writeSectionStartIfNecessary()
+        {
+            if (title_ != NULL)
+            {
+                context_.writeTitle(title_);
+                title_ = NULL;
+            }
+            if (!bDidOutput_)
+            {
+                if (header_ != NULL)
+                {
+                    context_.writeTextBlock(header_);
+                }
+                context_.writeOptionListStart();
+            }
+            bDidOutput_ = true;
+        }
+
+        const HelpWriterContext               &context_;
+        const CommonFormatterData             &common_;
+        boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
+        const char                            *title_;
+        const char                            *header_;
+        bool                                   bDidOutput_;
+
+        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsListFormatter);
+};
+
+OptionsListFormatter::OptionsListFormatter(
+        const HelpWriterContext   &context,
+        const CommonFormatterData &common,
+        const char                *title)
+    : context_(context), common_(common),
+      title_(title), header_(NULL), bDidOutput_(false)
+{
+    if (context.outputFormat() == eHelpOutputFormat_Console)
     {
-        genericOptionFormatter_.setColumnFirstLineOffset(3, 1);
+        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);
     }
-
-    context.outputFile().writeString(genericOptionFormatter_.formatRow());
 }
 
-void OptionsConsoleFormatter::formatSelectionOption(
-        const HelpWriterContext &context, const OptionInfo &option)
+void OptionsListFormatter::formatOption(const OptionInfo &option)
 {
-    File &file = context.outputFile();
-
-    selectionOptionFormatter_.clear();
-    std::string name(formatString("-%s", option.name().c_str()));
-    selectionOptionFormatter_.addColumnLine(0, name);
-    selectionOptionFormatter_.addColumnLine(1, context.substituteMarkup(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)
+    writeSectionStartIfNecessary();
+    std::string name, value;
+    formatOptionNameAndValue(option, &name, &value);
+    std::string defaultValue(defaultOptionValue(option));
+    std::string info;
+    if (!defaultValue.empty())
+    {
+        info = "(" + defaultValue + ")";
+    }
+    const FileNameOptionInfo *fileOption = option.toType<FileNameOptionInfo>();
+    if (fileOption != NULL)
+    {
+        const bool bAbbrev = (context_.outputFormat() == eHelpOutputFormat_Console);
+        if (!info.empty())
+        {
+            info.append(" ");
+        }
+        info.append("(");
+        info.append(fileOptionFlagsAsString(*fileOption, bAbbrev));
+        info.append(")");
+    }
+    std::string description(descriptionWithOptionDetails(common_, option));
+    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 (!info.empty())
+        {
+            consoleFormatter_->addColumnLine(2, info);
+        }
+        consoleFormatter_->addColumnHelpTextBlock(3, context_, description);
+        context_.outputFile().writeString(consoleFormatter_->formatRow());
+    }
+    else
+    {
+        if (!info.empty())
+        {
+            value.append(" ");
+            value.append(info);
+        }
+        context_.writeOptionItem("-" + name, value, description);
     }
 }
 
+//! \}
+
 }   // namespace
 
 /********************************************************************
@@ -486,12 +609,17 @@ class CommandLineHelpWriter::Impl
         //! Sets the Options object to use for generating help.
         explicit Impl(const Options &options);
 
+        //! Format the list of known issues.
+        void formatBugs(const HelpWriterContext &context);
+
         //! Options object to use for generating help.
-        const Options          &options_;
+        const Options               &options_;
+        //! List of bugs/knows issues.
+        ConstArrayRef<const char *>  bugs_;
         //! Time unit to show in descriptions.
-        std::string             timeUnit_;
+        std::string                  timeUnit_;
         //! Whether to write descriptions to output.
-        bool                    bShowDescriptions_;
+        bool                         bShowDescriptions_;
 };
 
 CommandLineHelpWriter::Impl::Impl(const Options &options)
@@ -500,6 +628,45 @@ CommandLineHelpWriter::Impl::Impl(const Options &options)
 {
 }
 
+void CommandLineHelpWriter::Impl::formatBugs(const HelpWriterContext &context)
+{
+    if (bugs_.empty())
+    {
+        return;
+    }
+    context.writeTitle("Known Issues");
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        context.writeTextBlock("[UL]");
+    }
+    ConstArrayRef<const char *>::const_iterator i;
+    for (i = bugs_.begin(); i != bugs_.end(); ++i)
+    {
+        const char *const bug = *i;
+        // TODO: The context should be able to do this also for console output, but
+        // that requires a lot more elaborate parser for the markup.
+        if (context.outputFormat() == eHelpOutputFormat_Console)
+        {
+            TextLineWrapperSettings settings;
+            settings.setIndent(2);
+            settings.setFirstLineIndent(0);
+            settings.setLineLength(78);
+            context.outputFile().writeLine(
+                    context.substituteMarkupAndWrapToString(
+                            settings, formatString("* %s", bug)));
+        }
+        else
+        {
+            context.writeTextBlock(formatString("[LI]%s", bug));
+        }
+    }
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        context.writeTextBlock("[ul]");
+    }
+}
+
+
 /********************************************************************
  * CommandLineHelpWriter
  */
@@ -513,52 +680,67 @@ CommandLineHelpWriter::~CommandLineHelpWriter()
 {
 }
 
-CommandLineHelpWriter &CommandLineHelpWriter::setShowDescriptions(bool bSet)
+CommandLineHelpWriter &
+CommandLineHelpWriter::setShowDescriptions(bool bSet)
 {
     impl_->bShowDescriptions_ = bSet;
     return *this;
 }
 
-CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
+CommandLineHelpWriter &
+CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
 {
     impl_->timeUnit_ = timeUnit;
     return *this;
 }
 
+CommandLineHelpWriter &
+CommandLineHelpWriter::setKnownIssues(const ConstArrayRef<const char *> &bugs)
+{
+    impl_->bugs_ = bugs;
+    return *this;
+}
+
 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())
+    if (context.isCompletionExport())
     {
-        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"));
+        context.shellCompletionWriter().writeModuleCompletions(
+                context.moduleDisplayName(), impl_->options_);
+        return;
     }
-    OptionsFilter filter(writerContext, formatter.get());
+    const HelpWriterContext &writerContext = context.writerContext();
+    OptionsFilter            filter;
     filter.setShowHidden(context.showHidden());
 
+    {
+        writerContext.writeTitle("Synopsis");
+        SynopsisFormatter synopsisFormatter(writerContext);
+        synopsisFormatter.start(context.moduleDisplayName());
+        filter.formatSelected(OptionsFilter::eSelectFileOptions,
+                              &synopsisFormatter, impl_->options_);
+        filter.formatSelected(OptionsFilter::eSelectOtherOptions,
+                              &synopsisFormatter, impl_->options_);
+        synopsisFormatter.finish();
+    }
+
     if (impl_->bShowDescriptions_)
     {
-        File &file = writerContext.outputFile();
-        file.writeLine("DESCRIPTION");
-        file.writeLine("-----------");
-        file.writeLine();
-        filter.formatSelected(OptionsFilter::eSelectDescriptions,
-                              impl_->options_);
+        DescriptionsFormatter descriptionFormatter(writerContext);
+        descriptionFormatter.format(impl_->options_, "Description");
     }
+    CommonFormatterData    common(impl_->timeUnit_.c_str());
+    OptionsListFormatter   formatter(writerContext, common, "Options");
+    formatter.startSection("Options to specify input and output files:[PAR]");
     filter.formatSelected(OptionsFilter::eSelectFileOptions,
-                          impl_->options_);
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+    formatter.startSection("Other options:[PAR]");
     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
-                          impl_->options_);
-    filter.formatSelected(OptionsFilter::eSelectSelectionOptions,
-                          impl_->options_);
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+
+    impl_->formatBugs(writerContext);
 }
 
 } // namespace gmx