Update help output
authorTeemu Murtola <teemu.murtola@gmail.com>
Thu, 23 Apr 2015 10:31:22 +0000 (13:31 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Fri, 12 Jun 2015 12:58:41 +0000 (14:58 +0200)
Various changes to console help output (and some to man output) based on
feedback in #1687:
 - People seem to prefer a centered startup header with some ASCII art,
   so made the first output line just like that (except if running with
   -quiet), and center the list of authors.
 - Always start the option description at a new line.
 - Give more space to various parts of the option list so that they are
   more likely to align up.
 - Split the file listing based on input/output type.  This causes,
   e.g., some logically related options (e.g., -cpi and -cpo) to get
   separated in the output, but this seems to be the preferred approach.
 - Add formatting to the synopsis on man pages (similar to how the
   options appeared in the 4.6-era man pages, except that reasonable
   line wrapping is still there).

Clean up the design for formatting the options list; now the actual
formatting code is better encapsulated in HelpWriterContext.

The user-visible scope of this change is limited to changing behavior
that changed between 4.6 and 5.0.  Thus, the changes here should be
sufficient to close #1687.

Change-Id: I349fd9021472f064f5797441090a3f3864868280

src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/commandline/tests/refdata/CommandLineHelpModuleTest_ExportsHelp.xml
src/gromacs/commandline/tests/refdata/CommandLineHelpModuleTest_PrintsGeneralHelp.xml
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/gmxlib/copyrite.cpp
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/onlinehelp/helpwritercontext.h

index 788991565a568be7469f4f055d455342f2646cd4..addc8c8361c0e01a1c100a0d35990088cbd7cd4b 100644 (file)
@@ -51,7 +51,6 @@
 #include <boost/scoped_ptr.hpp>
 
 #include "gromacs/commandline/cmdlinehelpcontext.h"
-#include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/filenameoption.h"
@@ -174,7 +173,9 @@ class OptionsFilter : public OptionsVisitor
         //! Specifies the type of output that formatSelected() produces.
         enum FilterType
         {
-            eSelectFileOptions,
+            eSelectInputFileOptions,
+            eSelectInputOutputFileOptions,
+            eSelectOutputFileOptions,
             eSelectOtherOptions
         };
 
@@ -233,9 +234,26 @@ void OptionsFilter::visitOption(const OptionInfo &option)
     {
         return;
     }
-    if (option.isType<FileNameOptionInfo>())
+    const FileNameOptionInfo *const fileOption = option.toType<FileNameOptionInfo>();
+    if (fileOption != NULL && fileOption->isInputFile())
+    {
+        if (filterType_ == eSelectInputFileOptions)
+        {
+            formatter_->formatOption(option);
+        }
+        return;
+    }
+    if (fileOption != NULL && fileOption->isInputOutputFile())
+    {
+        if (filterType_ == eSelectInputOutputFileOptions)
+        {
+            formatter_->formatOption(option);
+        }
+        return;
+    }
+    if (fileOption != NULL && fileOption->isOutputFile())
     {
-        if (filterType_ == eSelectFileOptions)
+        if (filterType_ == eSelectOutputFileOptions)
         {
             formatter_->formatOption(option);
         }
@@ -323,25 +341,17 @@ std::string
 fileOptionFlagsAsString(const FileNameOptionInfo &option, bool bAbbrev)
 {
     std::string type;
-    if (option.isInputOutputFile())
-    {
-        type = bAbbrev ? "In/Out" : "Input/Output";
-    }
-    else if (option.isInputFile())
-    {
-        type = "Input";
-    }
-    else if (option.isOutputFile())
-    {
-        type = "Output";
-    }
     if (!option.isRequired())
     {
-        type += bAbbrev ? ", Opt." : ", Optional";
+        type = bAbbrev ? "Opt." : "Optional";
     }
     if (option.isLibraryFile())
     {
-        type += bAbbrev ? ", Lib." : ", Library";
+        if (!type.empty())
+        {
+            type.append(", ");
+        }
+        type.append(bAbbrev ? "Lib." : "Library");
     }
     return type;
 }
@@ -377,7 +387,8 @@ 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)
+            : context_(context), bFormatted_(false), lineLength_(0), indent_(0),
+              currentLength_(0)
         {
         }
 
@@ -390,6 +401,7 @@ class SynopsisFormatter : public OptionsFormatterInterface
 
     private:
         const HelpWriterContext &context_;
+        bool                     bFormatted_;
         int                      lineLength_;
         int                      indent_;
         int                      currentLength_;
@@ -409,9 +421,10 @@ void SynopsisFormatter::start(const char *name)
             file.writeString(name);
             break;
         case eHelpOutputFormat_Rst:
+            bFormatted_ = true;
             lineLength_ = 74;
             indent_    += 4;
-            file.writeLine("::");
+            file.writeLine(".. parsed-literal::");
             file.writeLine();
             file.writeString("    ");
             file.writeString(name);
@@ -432,14 +445,16 @@ void SynopsisFormatter::formatOption(const OptionInfo &option)
 {
     std::string name, value;
     formatOptionNameAndValue(option, &name, &value);
-    std::string fullOptionText(" [-" + name);
+    int         totalLength = name.length() + 4;
+    std::string fullOptionText
+        = formatString(" [%s-%s", bFormatted_ ? ":strong:`" : "", name.c_str());
     if (!value.empty())
     {
-        fullOptionText.append(" ");
+        fullOptionText.append(bFormatted_ ? "` :emphasis:`" : " ");
         fullOptionText.append(value);
+        totalLength += value.length() + 1;
     }
-    fullOptionText.append("]");
-    const int   totalLength = fullOptionText.size();
+    fullOptionText.append(bFormatted_ ? "`]" : "]");
 
     File       &file = context_.outputFile();
     currentLength_ += totalLength;
@@ -506,7 +521,6 @@ class OptionsListFormatter : public OptionsFormatterInterface
 
         const HelpWriterContext               &context_;
         const CommonFormatterData             &common_;
-        boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
         const char                            *title_;
         const char                            *header_;
         bool                                   bDidOutput_;
@@ -521,63 +535,23 @@ OptionsListFormatter::OptionsListFormatter(
     : context_(context), common_(common),
       title_(title), header_(NULL), bDidOutput_(false)
 {
-    if (context.outputFormat() == eHelpOutputFormat_Console)
-    {
-        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 OptionsListFormatter::formatOption(const OptionInfo &option)
 {
     writeSectionStartIfNecessary();
-    std::string name, value;
+    std::string               name, value;
     formatOptionNameAndValue(option, &name, &value);
-    std::string defaultValue(defaultOptionValue(option));
-    std::string info;
-    if (!defaultValue.empty())
-    {
-        info = "(" + defaultValue + ")";
-    }
+    std::string               defaultValue(defaultOptionValue(option));
+    std::string               info;
     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(")");
+        info = fileOptionFlagsAsString(*fileOption, bAbbrev);
     }
     std::string description(descriptionWithOptionDetails(common_, option));
-    if (context_.outputFormat() == eHelpOutputFormat_Console)
-    {
-        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);
-    }
+    context_.writeOptionItem("-" + name, value, defaultValue, info, description);
 }
 
 //! \}
@@ -684,7 +658,11 @@ void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
         writerContext.writeTitle("Synopsis");
         SynopsisFormatter synopsisFormatter(writerContext);
         synopsisFormatter.start(context.moduleDisplayName());
-        filter.formatSelected(OptionsFilter::eSelectFileOptions,
+        filter.formatSelected(OptionsFilter::eSelectInputFileOptions,
+                              &synopsisFormatter, impl_->options_);
+        filter.formatSelected(OptionsFilter::eSelectInputOutputFileOptions,
+                              &synopsisFormatter, impl_->options_);
+        filter.formatSelected(OptionsFilter::eSelectOutputFileOptions,
                               &synopsisFormatter, impl_->options_);
         filter.formatSelected(OptionsFilter::eSelectOtherOptions,
                               &synopsisFormatter, impl_->options_);
@@ -698,11 +676,19 @@ void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
     }
     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,
+    formatter.startSection("Options to specify input files:");
+    filter.formatSelected(OptionsFilter::eSelectInputFileOptions,
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+    formatter.startSection("Options to specify input/output files:");
+    filter.formatSelected(OptionsFilter::eSelectInputOutputFileOptions,
+                          &formatter, impl_->options_);
+    formatter.finishSection();
+    formatter.startSection("Options to specify output files:");
+    filter.formatSelected(OptionsFilter::eSelectOutputFileOptions,
                           &formatter, impl_->options_);
     formatter.finishSection();
-    formatter.startSection("Other options:[PAR]");
+    formatter.startSection("Other options:");
     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
                           &formatter, impl_->options_);
     formatter.finishSection();
index b2612d7fb7a4aaf4b7c2794f4521e5d1af32d91c..3bed43d1743d32b5fc31b17311a35c054fe4a54d 100644 (file)
@@ -37,9 +37,9 @@ test module
 ===========
 Synopsis
 --------
-::
+.. parsed-literal::
 
-    test module [-int <int>]
+    test module [:strong:`-int` :emphasis:`<int>`]
 
 Description
 -----------
@@ -70,7 +70,7 @@ test other
 ==========
 Synopsis
 --------
-::
+.. parsed-literal::
 
     test other
 
index b8c655d7fa7017870eb3bf959c07a3fd88b03543..6820ba327ec23f29c713f21569aca9ef53d01b25 100644 (file)
@@ -11,12 +11,18 @@ OPTIONS
 
 Other options:
 
- -[no]h          (no)       Print help and quit
- -[no]quiet      (no)       Do not print common startup info or quotes
- -[no]version    (no)       Print extended version information and quit
- -[no]copyright  (yes)      Print copyright information on startup
- -nice  <int>    (19)       Set the nicelevel (default depends on command)
- -[no]backup     (yes)      Write backups if output files exist
+ -[no]h                     (no)
+           Print help and quit
+ -[no]quiet                 (no)
+           Do not print common startup info or quotes
+ -[no]version               (no)
+           Print extended version information and quit
+ -[no]copyright             (yes)
+           Print copyright information on startup
+ -nice   <int>              (19)
+           Set the nicelevel (default depends on command)
+ -[no]backup                (yes)
+           Write backups if output files exist
 
 Additional help is available on the following topics:
     commands  List of available commands
index 8ac10f83fe5a6747b20285b2a3ec97c63de7aee8..86a8aadf4d37f0160e725fd1f23ae9293d7b6903 100644 (file)
@@ -9,18 +9,19 @@ gmx [-f [<.xtc/.trr/...>]] [-f2 [<.xtc/.trr/...>]] [-lib [<.xtc/.trr/...>]]
 
 OPTIONS
 
-Options to specify input and output files:
+Options to specify input files:
 
- -f     [<.xtc/.trr/...>] (path/to/long/trajectory/name.xtc) (Input)
-     File name option with a long value: xtc trr cpt gro g96 pdb tng
- -f2    [<.xtc/.trr/...>] (path/to/long/trajectory.xtc) (Input)
-     File name option with a long value: xtc trr cpt gro g96 pdb tng
- -lib   [<.xtc/.trr/...>] (path/to/long/trajectory/name.xtc) (Input, Opt., Lib.)
-     File name option with a long value and type: xtc trr cpt gro g96 pdb tng
- -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
+ -f      [<.xtc/.trr/...>]  (path/to/long/trajectory/name.xtc)
+           File name option with a long value: xtc trr cpt gro g96 pdb tng
+ -f2     [<.xtc/.trr/...>]  (path/to/long/trajectory.xtc)
+           File name option with a long value: xtc trr cpt gro g96 pdb tng
+ -lib    [<.xtc/.trr/...>]  (path/to/long/trajectory/name.xtc) (Opt., Lib.)
+           File name option with a long value and type: xtc trr cpt gro g96
+           pdb tng
+ -longfileopt [<.dat>]      (deffile.dat)    (Opt.)
+           File name option with a long name
+ -longfileopt2 [<.dat>]     (path/to/long/file/name.dat) (Opt., Lib.)
+           File name option with multiple long fields
 
 ]]></String>
 </ReferenceData>
index 92805bff8e58291cacde1a9a7e304034c093ca43..186f79e92114fec24bcde9a6d2412afb7de9ee9b 100644 (file)
@@ -10,11 +10,13 @@ OPTIONS
 
 Other 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)
+ -[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 57f8bcf19dc236c40a98cd27891402fbf2d84887..baa4db004727a43465702b0e797ac68b57d535b6 100644 (file)
@@ -20,9 +20,12 @@ OPTIONS
 
 Other options:
 
- -sub1  <int>               Option in subsection 1
- -sub2  <int>               Option in subsection 2
- -main  <int>               Option in main section
+ -sub1   <int>
+           Option in subsection 1
+ -sub2   <int>
+           Option in subsection 2
+ -main   <int>
+           Option in main section
 
 ]]></String>
 </ReferenceData>
index d60500d2bf4cf2fec35c14c28e63db1fc6641822..a3e570b815764b5750c915e7647e583e9975177d 100644 (file)
@@ -11,27 +11,45 @@ gmx [-f [<.xtc/.trr/...>]] [-mult [<.xtc/.trr/...> [...]]] [-lib [<.dat>]]
 
 OPTIONS
 
-Options to specify input and output files:
+Options to specify input files:
 
- -f     [<.xtc/.trr/...>] (traj.xtc) (Input)
-     Input file description: xtc trr cpt gro g96 pdb tng
- -mult  [<.xtc/.trr/...> [...]] (traj.xtc) (Input, Opt.)
-     Multiple file description: xtc trr cpt gro g96 pdb tng
- -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
+ -f      [<.xtc/.trr/...>]  (traj.xtc)
+           Input file description: xtc trr cpt gro g96 pdb tng
+ -mult   [<.xtc/.trr/...> [...]] (traj.xtc)  (Opt.)
+           Multiple file description: xtc trr cpt gro g96 pdb tng
+ -lib    [<.dat>]           (libdata.dat)    (Opt., Lib.)
+           Library file description
+
+Options to specify input/output files:
+
+ -io     [<.dat>]           (inout.dat)      (Opt.)
+           Input/Output file description
+
+Options to specify output files:
+
+ -o      <.xvg>                              (Opt.)
+           Output file description
 
 Other 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
+ -[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>
index b5ffff65fda4f32327f0fb1f3d33626741e9321d..97720b65133d8d495eb6a94086e578613bf28963 100644 (file)
@@ -45,6 +45,8 @@
 #include <string.h>
 #include <time.h>
 
+#include <algorithm>
+
 #ifdef HAVE_LIBMKL
 #include <mkl.h>
 #endif
@@ -160,6 +162,17 @@ void cool_quote(char *retstring, int retsize, int *cqnum)
     sfree(tmpstr);
 }
 
+static int centeringOffset(int width, int length)
+{
+    return std::max(width - length, 0) / 2;
+}
+
+static void printCentered(FILE *fp, int width, const char *text)
+{
+    const int offset = centeringOffset(width, std::strlen(text));
+    fprintf(fp, "%*s%s", offset, "", text);
+}
+
 static void printCopyright(FILE *fp)
 {
     static const char * const Contributors[] = {
@@ -215,18 +228,27 @@ static void printCopyright(FILE *fp)
 #define NLICENSE (int)asize(LicenseText)
 #endif
 
-    fprintf(fp, "GROMACS is written by:\n");
+    printCentered(fp, 78, "GROMACS is written by:");
+    fprintf(fp, "\n");
     for (int i = 0; i < NCONTRIBUTORS; )
     {
         for (int j = 0; j < 4 && i < NCONTRIBUTORS; ++j, ++i)
         {
-            fprintf(fp, "%-18s ", Contributors[i]);
+            const int width = 18;
+            char      buf[30];
+            const int offset = centeringOffset(width, strlen(Contributors[i]));
+            GMX_RELEASE_ASSERT(strlen(Contributors[i]) + offset < asize(buf),
+                               "Formatting buffer is not long enough");
+            std::fill(buf, buf+width, ' ');
+            std::strcpy(buf+offset, Contributors[i]);
+            fprintf(fp, " %-*s", width, buf);
         }
         fprintf(fp, "\n");
     }
-    fprintf(fp, "and the project leaders:\n");
-    fprintf(fp, "Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel\n");
+    printCentered(fp, 78, "and the project leaders:");
     fprintf(fp, "\n");
+    printCentered(fp, 78, "Mark Abraham, Berk Hess, Erik Lindahl, and David van der Spoel");
+    fprintf(fp, "\n\n");
     for (int i = 0; i < NCR; ++i)
     {
         fprintf(fp, "%s\n", CopyrightText[i]);
@@ -791,26 +813,29 @@ void printBinaryInformation(FILE                            *fp,
     {
         fprintf(fp, "%sCreated by:%s\n", prefix, suffix);
     }
+    // TODO: It would be nice to know here whether we are really running a
+    // Gromacs binary or some other binary that is calling Gromacs; we
+    // could then print "%s is part of GROMACS" or some alternative text.
+    std::string title
+        = formatString(":-) GROMACS - %s, %s%s (-:", name, gmx_version(), precisionString);
+    const int   indent
+        = centeringOffset(78 - strlen(prefix) - strlen(suffix), title.length()) + 1;
+    fprintf(fp, "%s%*c%s%s\n", prefix, indent, ' ', title.c_str(), suffix);
+    fprintf(fp, "\n");
     if (settings.bCopyright_)
     {
         GMX_RELEASE_ASSERT(prefix[0] == '\0' && suffix[0] == '\0',
                            "Prefix/suffix not supported with copyright");
+        printCopyright(fp);
+        fprintf(fp, "\n");
         // This line is printed again after the copyright notice to make it
         // appear together with all the other information, so that it is not
         // necessary to read stuff above the copyright notice.
         // The line above the copyright notice puts the copyright notice is
         // context, though.
-        // TODO: It would be nice to know here whether we are really running a
-        // Gromacs binary or some other binary that is calling Gromacs; we
-        // could then print "%s is part of GROMACS" or some alternative text.
-        fprintf(fp, "%sGROMACS:    %s, %s%s%s\n", prefix, name,
+        fprintf(fp, "%sGROMACS:      %s, %s%s%s\n", prefix, name,
                 gmx_version(), precisionString, suffix);
-        fprintf(fp, "\n");
-        printCopyright(fp);
-        fprintf(fp, "\n");
     }
-    fprintf(fp, "%sGROMACS:      %s, %s%s%s\n", prefix, name,
-            gmx_version(), precisionString, suffix);
     const char *const binaryPath = programContext.fullBinaryPath();
     if (!gmx::isNullOrEmpty(binaryPath))
     {
index 1f3101db4ca1ef4bdd703856205f92c9be40fa9e..f4deb7918619be1e1dd3cb568a7312a1d80cdb4c 100644 (file)
@@ -51,6 +51,7 @@
 
 #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"
@@ -433,21 +434,48 @@ class HelpWriterContext::Impl
          *
          * \ingroup module_onlinehelp
          */
-        struct SharedState
+        class SharedState
         {
-            //! Initializes the state with the given parameters.
-            SharedState(File *file, HelpOutputFormat format,
-                        const HelpLinks *links)
-                : file_(*file), format_(format), links_(links)
-            {
-            }
-
-            //! Output file to which the help is written.
-            File                   &file_;
-            //! Output format for the help output.
-            HelpOutputFormat        format_;
-            //! Links to use.
-            const HelpLinks        *links_;
+            public:
+                //! Initializes the state with the given parameters.
+                SharedState(File *file, HelpOutputFormat format,
+                            const HelpLinks *links)
+                    : file_(*file), format_(format), links_(links)
+                {
+                }
+
+                /*! \brief
+                 * Returns a formatter for formatting options lists for console
+                 * output.
+                 *
+                 * The formatter is lazily initialized on first access.
+                 */
+                TextTableFormatter &consoleOptionsFormatter() const
+                {
+                    GMX_RELEASE_ASSERT(format_ == eHelpOutputFormat_Console,
+                                       "Accessing console formatter for non-console output");
+                    if (!consoleOptionsFormatter_)
+                    {
+                        consoleOptionsFormatter_.reset(new TextTableFormatter());
+                        consoleOptionsFormatter_->setFirstColumnIndent(1);
+                        consoleOptionsFormatter_->addColumn(NULL, 7, false);
+                        consoleOptionsFormatter_->addColumn(NULL, 18, false);
+                        consoleOptionsFormatter_->addColumn(NULL, 16, false);
+                        consoleOptionsFormatter_->addColumn(NULL, 28, false);
+                    }
+                    return *consoleOptionsFormatter_;
+                }
+
+                //! Output file to which the help is written.
+                File                   &file_;
+                //! Output format for the help output.
+                HelpOutputFormat        format_;
+                //! Links to use.
+                const HelpLinks        *links_;
+
+            private:
+                //! Formatter for console output options.
+                mutable boost::scoped_ptr<TextTableFormatter> consoleOptionsFormatter_;
         };
 
         struct ReplaceItem
@@ -681,19 +709,52 @@ void HelpWriterContext::writeOptionListStart() const
 }
 
 void HelpWriterContext::writeOptionItem(const std::string &name,
-                                        const std::string &args,
+                                        const std::string &value,
+                                        const std::string &defaultValue,
+                                        const std::string &info,
                                         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"));
+        {
+            TextTableFormatter &formatter(impl_->state_->consoleOptionsFormatter());
+            formatter.clear();
+            formatter.addColumnLine(0, name);
+            formatter.addColumnLine(1, value);
+            if (!defaultValue.empty())
+            {
+                formatter.addColumnLine(2, "(" + defaultValue + ")");
+            }
+            if (!info.empty())
+            {
+                formatter.addColumnLine(3, "(" + info + ")");
+            }
+            TextLineWrapperSettings settings;
+            settings.setIndent(11);
+            settings.setLineLength(78);
+            std::string formattedDescription
+                = substituteMarkupAndWrapToString(settings, description);
+            file.writeLine(formatter.formatRow());
+            file.writeLine(formattedDescription);
             break;
+        }
         case eHelpOutputFormat_Rst:
         {
+            std::string args(value);
+            if (!defaultValue.empty())
+            {
+                args.append(" (");
+                args.append(defaultValue);
+                args.append(")");
+            }
+            if (!info.empty())
+            {
+                args.append(" (");
+                args.append(info);
+                args.append(")");
+            }
             file.writeLine(formatString("``%s`` %s", name.c_str(), args.c_str()));
             TextLineWrapperSettings settings;
             settings.setIndent(4);
index ed480bb818282d7d55de4ffd99cd7887cba254ff..1cbce8e6b5e5533eaeb4af8e7b8565f15bcc4d9d 100644 (file)
@@ -263,12 +263,16 @@ class HelpWriterContext
          * 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] value        Placeholder for option value.
+         * \param[in] defaultValue Default value for the option.
+         * \param[in] info         Additional (brief) info/attributes for the
+         *      option.
          * \param[in] description  Full description of the option.
          */
-        void writeOptionItem(const std::string &name, const std::string &args,
-                             const std::string &description) const;
+        void writeOptionItem(
+            const std::string &name, const std::string &value,
+            const std::string &defaultValue, const std::string &info,
+            const std::string &description) const;
         /*! \brief
          * Finishes writing a list of options.
          *