Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpwriter.cpp
index 36e66b9119de4792f10a8eb778b434472cc76d85..0c79bd393451006b0e4c5161ced2b0ab4170a1dd 100644 (file)
  * \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/commandline/shellcompletions.h"
 #include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/options.h"
 #include "gromacs/options/optionsvisitor.h"
 #include "gromacs/options/timeunitmanager.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
 {
 
@@ -67,6 +74,69 @@ 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
  */
@@ -81,15 +151,8 @@ 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 option option.
+        virtual void formatOption(const OptionInfo &option) = 0;
 };
 
 /********************************************************************
@@ -111,7 +174,6 @@ class OptionsFilter : public OptionsVisitor
         //! Specifies the type of output that formatSelected() produces.
         enum FilterType
         {
-            eSelectDescriptions,
             eSelectFileOptions,
             eSelectOtherOptions
         };
@@ -119,16 +181,11 @@ class OptionsFilter : public OptionsVisitor
         /*! \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),
-              bShowHidden_(false), filterType_(eSelectOtherOptions),
-              title_(NULL), bDidOutput_(false)
+        OptionsFilter()
+            : formatter_(NULL), filterType_(eSelectOtherOptions),
+              bShowHidden_(false)
         {
         }
 
@@ -139,67 +196,32 @@ class OptionsFilter : public OptionsVisitor
         }
 
         //! Formats selected options using the formatter.
-        void formatSelected(FilterType type, const Options &options,
-                            const char *title);
+        void formatSelected(FilterType                 type,
+                            OptionsFormatterInterface *formatter,
+                            const Options             &options);
 
         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_;
-        bool                            bShowHidden_;
+        OptionsFormatterInterface      *formatter_;
         FilterType                      filterType_;
-        const char                     *title_;
-        bool                            bDidOutput_;
+        bool                            bShowHidden_;
 
         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
 };
 
-void OptionsFilter::formatSelected(FilterType type, const Options &options,
-                                   const char *title)
+void OptionsFilter::formatSelected(FilterType                 type,
+                                   OptionsFormatterInterface *formatter,
+                                   const Options             &options)
 {
+    formatter_  = formatter;
     filterType_ = type;
-    title_      = title;
-    bDidOutput_ = false;
     visitSubSection(options);
-    if (bDidOutput_)
-    {
-        if (type != eSelectDescriptions)
-        {
-            context_.writeOptionListEnd();
-        }
-        context_.outputFile().writeLine();
-    }
 }
 
 void OptionsFilter::visitSubSection(const Options &section)
 {
-    if (filterType_ == eSelectDescriptions)
-    {
-        if (!section.description().empty())
-        {
-            if (bDidOutput_)
-            {
-                context_.outputFile().writeLine();
-            }
-            else
-            {
-                writeTitleIfSet();
-            }
-            formatter_.formatDescription(context_, section);
-            bDidOutput_ = true;
-        }
-    }
-
     OptionsIterator iterator(section);
     iterator.acceptSubSections(this);
     iterator.acceptOptions(this);
@@ -215,26 +237,13 @@ void OptionsFilter::visitOption(const OptionInfo &option)
     {
         if (filterType_ == eSelectFileOptions)
         {
-            if (!bDidOutput_)
-            {
-                writeTitleIfSet();
-                context_.writeOptionListStart();
-            }
-            formatter_.formatFileOption(context_,
-                                        *option.toType<FileNameOptionInfo>());
-            bDidOutput_ = true;
+            formatter_->formatOption(option);
         }
         return;
     }
     if (filterType_ == eSelectOtherOptions)
     {
-        if (!bDidOutput_)
-        {
-            writeTitleIfSet();
-            context_.writeOptionListStart();
-        }
-        formatter_.formatOption(context_, option);
-        bDidOutput_ = true;
+        formatter_->formatOption(option);
         return;
     }
 }
@@ -258,6 +267,33 @@ class CommonFormatterData
  * Helper functions
  */
 
+//! Formats option name and value.
+void formatOptionNameAndValue(const OptionInfo &option, std::string *name,
+                              std::string *value)
+{
+    *name  = option.name();
+    *value = "<" + option.type() + ">";
+    if (option.isType<FileNameOptionInfo>())
+    {
+        // TODO: Make these work also for other option types.
+        if (option.maxValueCount() != 1)
+        {
+            *value += " [...]";
+        }
+        if (option.minValueCount() == 0)
+        {
+            *value = "[" + *value + "]";
+        }
+    }
+    if (option.isType<BooleanOptionInfo>())
+    {
+        *name = "[no]" + *name;
+        // Old command-line parser doesn't accept any values for these.
+        // value = "[" + value + "]";
+        value->clear();
+    }
+}
+
 //! Formats the default option value as a string.
 std::string
 defaultOptionValue(const OptionInfo &option)
@@ -330,124 +366,227 @@ descriptionWithOptionDetails(const CommonFormatterData &common,
 }
 
 /********************************************************************
- * OptionsExportFormatter
+ * OptionsSynopsisFormatter
  */
 
 /*! \brief
- * Formatter implementation for help export.
+ * Formatter implementation for synopsis.
  */
-class OptionsExportFormatter : public OptionsFormatterInterface
+class SynopsisFormatter : public OptionsFormatterInterface
 {
     public:
-        //! Creates a helper object for formatting options.
-        OptionsExportFormatter(const CommonFormatterData &common, bool bConsole);
+        //! Creates a helper object for formatting the synopsis.
+        explicit SynopsisFormatter(const HelpWriterContext &context)
+            : context_(context), lineLength_(0), indent_(0), currentLength_(0)
+        {
+        }
+
+        //! Starts formatting the synopsis.
+        void start(const char *name);
+        //! Finishes formatting the synopsis.
+        void finish();
 
-        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 formatOption(const OptionInfo &option);
 
     private:
-        const CommonFormatterData             &common_;
-        boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
+        const HelpWriterContext &context_;
+        int                      lineLength_;
+        int                      indent_;
+        int                      currentLength_;
 
-        GMX_DISALLOW_COPY_AND_ASSIGN(OptionsExportFormatter);
+        GMX_DISALLOW_COPY_AND_ASSIGN(SynopsisFormatter);
 };
 
-OptionsExportFormatter::OptionsExportFormatter(
-        const CommonFormatterData &common, bool bConsole)
-    : common_(common)
+void SynopsisFormatter::start(const char *name)
 {
-    if (bConsole)
+    currentLength_ = std::strlen(name) + 1;
+    indent_        = std::min(currentLength_, 13);
+    File &file = context_.outputFile();
+    switch (context_.outputFormat())
     {
-        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);
+        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"));
     }
 }
 
-void OptionsExportFormatter::formatDescription(
-        const HelpWriterContext &context, const Options &section)
+void SynopsisFormatter::finish()
 {
-    // TODO: Print title for the section?
-    context.writeTextBlock(section.description());
+    File &file = context_.outputFile();
+    file.writeLine();
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
+    {
+        file.writeLine("</pre>");
+    }
+    file.writeLine();
 }
 
-void OptionsExportFormatter::formatFileOption(
-        const HelpWriterContext &context, const FileNameOptionInfo &option)
+void SynopsisFormatter::formatOption(const OptionInfo &option)
 {
-    const bool  bAbbrev = (context.outputFormat() == eHelpOutputFormat_Console);
-    std::string value("<" + option.type() + ">");
-    if (option.maxValueCount() != 1)
+    std::string name, value;
+    formatOptionNameAndValue(option, &name, &value);
+    std::string fullOptionText(" [-" + name);
+    if (!value.empty())
     {
-        value += " [...]";
+        fullOptionText.append(" ");
+        fullOptionText.append(value);
     }
-    if (option.minValueCount() == 0)
+    fullOptionText.append("]");
+    const int   totalLength = fullOptionText.size();
+
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
     {
-        value = "[" + value + "]";
+        value = replaceAll(value, "<", "&lt;");
+        value = replaceAll(value, ">", "&gt;");
     }
-    std::string defaultValue(defaultOptionValue(option));
-    std::string info = "(" + fileOptionFlagsAsString(option, bAbbrev) + ")";
-    if (!defaultValue.empty())
+
+    File &file = context_.outputFile();
+    currentLength_ += totalLength;
+    if (currentLength_ >= lineLength_)
     {
-        info = "(" + defaultValue + ") " + info;
+        file.writeString(formatString("\n%*c", indent_ - 1, ' '));
+        currentLength_ = indent_ - 1 + totalLength;
     }
-    std::string description(option.formatDescription());
+    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 (bDidOutput_)
+            {
+                context_.writeOptionListEnd();
+                context_.outputFile().writeLine();
+            }
+        }
+
+        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)
     {
-        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());
-    }
-    else
-    {
-        value += " " + info;
-        context.writeOptionItem("-" + option.name(), value, description);
+        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);
     }
 }
 
-void OptionsExportFormatter::formatOption(
-        const HelpWriterContext &context, const OptionInfo &option)
+void OptionsListFormatter::formatOption(const OptionInfo &option)
 {
-    std::string name(option.name());
-    std::string value("<" + option.type() + ">");
+    writeSectionStartIfNecessary();
+    std::string name, value;
+    formatOptionNameAndValue(option, &name, &value);
     std::string defaultValue(defaultOptionValue(option));
-    std::string description(descriptionWithOptionDetails(common_, option));
-    if (option.isType<BooleanOptionInfo>())
+    std::string info;
+    if (!defaultValue.empty())
     {
-        name  = "[no]" + name;
-        // Old command-line parser doesn't accept any values for these.
-        // value = "[" + value + "]";
-        value.clear();
+        info = "(" + defaultValue + ")";
     }
-    if (context.outputFormat() == eHelpOutputFormat_Console)
+    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)
     {
         consoleFormatter_->clear();
         consoleFormatter_->addColumnLine(0, "-" + name);
         consoleFormatter_->addColumnLine(1, value);
-        if (!defaultValue.empty())
+        if (!info.empty())
         {
-            consoleFormatter_->addColumnLine(2, "(" + defaultValue + ")");
+            consoleFormatter_->addColumnLine(2, info);
         }
-        consoleFormatter_->addColumnHelpTextBlock(3, context, description);
-        context.outputFile().writeString(consoleFormatter_->formatRow());
+        consoleFormatter_->addColumnHelpTextBlock(3, context_, description);
+        context_.outputFile().writeString(consoleFormatter_->formatRow());
     }
     else
     {
-        if (!defaultValue.empty())
+        if (!info.empty())
         {
-            value += " (" + defaultValue + ")";
+            value.append(" ");
+            value.append(info);
         }
-        context.writeOptionItem("-" + name, value, description);
+        context_.writeOptionItem("-" + name, value, description);
     }
 }
 
@@ -470,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)
@@ -484,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
  */
@@ -497,18 +680,27 @@ 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)
 {
     if (context.isCompletionExport())
@@ -518,31 +710,37 @@ void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
         return;
     }
     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);
+    OptionsFilter            filter;
     filter.setShowHidden(context.showHidden());
 
-    File &file = writerContext.outputFile();
-    if (!bConsole)
     {
-        // TODO: Write a proper synopsis, with all the options.
         writerContext.writeTitle("Synopsis");
-        writerContext.writeTextBlock(context.moduleDisplayName());
-        file.writeLine("\n\n");
+        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_)
     {
-        filter.formatSelected(OptionsFilter::eSelectDescriptions,
-                              impl_->options_, "Description");
+        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_, "File Options");
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+    formatter.startSection("Other options:[PAR]");
     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
-                          impl_->options_, "Options");
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+
+    impl_->formatBugs(writerContext);
 }
 
 } // namespace gmx