Implement synopsis for help output
authorTeemu Murtola <teemu.murtola@gmail.com>
Tue, 4 Feb 2014 18:41:19 +0000 (20:41 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Sun, 9 Feb 2014 12:38:13 +0000 (13:38 +0100)
Print all the options into the synopsis, for all the output formats
(console, man pages, and HTML).  Formatting is not the nicest possible,
but surely better than what it was (in wman.cpp, the synopsis is only
printed into the man pages, with no wrapping whatsoever).

Part of #969.

Change-Id: Ic09ac91b3d5b5e2d42d41b83935d54c894eb8e97

src/gromacs/commandline/cmdlinehelpmodule.cpp
src/gromacs/commandline/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/testutils/testoptions.cpp

index 2574edbd137025ee670464a349d6a7bb7b5bb995..1f957b925cfc3c96eb83f399e9366e6a3f600ae7 100644 (file)
@@ -120,9 +120,7 @@ struct RootHelpText
 // The first two are not used.
 const char        RootHelpText::name[]  = "";
 const char        RootHelpText::title[] = "";
-const char *const RootHelpText::text[]  = {
-    "Usage: [PROGRAM] [<options>] <command> [<args>]",
-};
+const char *const RootHelpText::text[]  = { "" };
 
 /*! \brief
  * Help topic that forms the root of the help tree for the help subcommand.
@@ -137,7 +135,8 @@ class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
          *
          * Does not throw.
          */
-        RootHelpTopic() : commonOptions_(NULL)
+        explicit RootHelpTopic(const std::string &binaryName)
+            : binaryName_(binaryName), commonOptions_(NULL)
         {
         }
 
@@ -150,6 +149,7 @@ class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
         virtual void writeHelp(const HelpWriterContext &context) const;
 
     private:
+        std::string                 binaryName_;
         const Options              *commonOptions_;
 
         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
@@ -164,10 +164,10 @@ void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
         GMX_THROW(NotImplementedError(
                           "Root help is not implemented for this output format"));
     }
-    writeBasicHelpTopic(context, *this, helpText());
-    context.outputFile().writeLine();
     {
         CommandLineHelpContext cmdlineContext(context);
+        cmdlineContext.setModuleDisplayName(binaryName_);
+        // TODO: Add <command> [<args>] into the synopsis.
         // TODO: Propagate the -hidden option here.
         CommandLineHelpWriter(*commonOptions_)
             .writeHelp(cmdlineContext);
@@ -723,7 +723,7 @@ CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
         const std::string                &binaryName,
         const CommandLineModuleMap       &modules,
         const CommandLineModuleGroupList &groups)
-    : rootTopic_(new RootHelpTopic), programContext_(programContext),
+    : rootTopic_(new RootHelpTopic(binaryName)), programContext_(programContext),
       binaryName_(binaryName), modules_(modules), groups_(groups),
       context_(NULL), moduleOverride_(NULL), bHidden_(false),
       outputOverride_(NULL)
index 86a9dee1c60568bfac39c1cb68e7fb8dde969c0e..c850cfe2fbc018dd6f8201fb56aede0601b2ae1c 100644 (file)
@@ -41,6 +41,9 @@
  */
 #include "cmdlinehelpwriter.h"
 
+#include <cstring>
+
+#include <algorithm>
 #include <string>
 
 #include <boost/scoped_ptr.hpp>
@@ -358,6 +361,103 @@ descriptionWithOptionDetails(const CommonFormatterData &common,
     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)
+        {
+        }
+
+        //! 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())
+    {
+        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 SynopsisFormatter::finish()
+{
+    File &file = context_.outputFile();
+    file.writeLine();
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
+    {
+        file.writeLine("</pre>");
+    }
+    file.writeLine();
+}
+
+void SynopsisFormatter::formatOption(const OptionInfo &option)
+{
+    std::string name, value;
+    formatOptionNameAndValue(option, &name, &value);
+    std::string fullOptionText(" [-" + name);
+    if (!value.empty())
+    {
+        fullOptionText.append(" ");
+        fullOptionText.append(value);
+    }
+    fullOptionText.append("]");
+    const int   totalLength = fullOptionText.size();
+
+    if (context_.outputFormat() == eHelpOutputFormat_Html)
+    {
+        value = replaceAll(value, "<", "&lt;");
+        value = replaceAll(value, ">", "&gt;");
+    }
+
+    File &file = context_.outputFile();
+    currentLength_ += totalLength;
+    if (currentLength_ >= lineLength_)
+    {
+        file.writeString(formatString("\n%*c", indent_ - 1, ' '));
+        currentLength_ = indent_ - 1 + totalLength;
+    }
+    file.writeString(fullOptionText);
+}
+
 /********************************************************************
  * OptionsListFormatter
  */
@@ -551,13 +651,15 @@ void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
     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_)
index f04be2aece597e46eb8b913af0e719969ae1abf6..b01af043e39815dc0cc1da129746d51c58a9ba39 100644 (file)
@@ -2,6 +2,11 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
+SYNOPSIS
+
+gmx [-f <.xtc/.trr/...>] [-f2 <.xtc/.trr/...>] [-lib [<.xtc/.trr/...>]]
+    [-longfileopt [<.dat>]] [-longfileopt2 [<.dat>]]
+
 FILE OPTIONS
 
  -f     <.xtc/.trr/...> (path/to/long/trajectory/name.xtc) (Input)
index 56311fb12d1b4acafcd90083bed3d8b68ba6acec..9ce40725899cd62546314d8eb7c69fa954440ebe 100644 (file)
@@ -2,6 +2,10 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
+SYNOPSIS
+
+gmx [-[no]longboolean] [-dvec <vector>] [-string <string>]
+
 OPTIONS
 
  -[no]longboolean (yes)     Boolean option with a long name
index 08132448c4ed6612038dbf89d935f50f6e02fd22..30e46df4262f13919fd42db7b5be78abfd9acf5b 100644 (file)
@@ -2,6 +2,10 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
+SYNOPSIS
+
+gmx [-sub1 <int>] [-sub2 <int>] [-main <int>]
+
 DESCRIPTION
 
 Description for main section.
index c4bb5f2748c15dc3c4053e3335b354a07896e91b..e81c9f60abfc29b510cbf958fdd084be76dca7c6 100644 (file)
@@ -2,6 +2,13 @@
 <?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
 <ReferenceData>
   <String Name="HelpText"><![CDATA[
+SYNOPSIS
+
+gmx [-f <.xtc/.trr/...>] [-mult [<.xtc/.trr/...> [...]]] [-lib [<.dat>]]
+    [-io [<.dat>]] [-o <.xvg>] [-[no]bool] [-[no]hidden] [-int <int>]
+    [-ivec <vector>] [-double <real>] [-dvec <vector>] [-time <time>]
+    [-string <string>] [-enum <enum>]
+
 FILE OPTIONS
 
  -f     <.xtc/.trr/...> (traj.xtc) (Input)
index 0457e1ca99054114f75161f53ed30dbee1e6469e..cf7f3bcee94680cba33d39f431304d2c0a18885b 100644 (file)
@@ -60,6 +60,7 @@
 #include "gromacs/utility/errorcodes.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
+#include "gromacs/utility/programcontext.h"
 
 #include "refdata.h"
 #include "testfilemanager.h"
@@ -129,6 +130,7 @@ void printHelp(const Options &options)
                  "to control the behavior of the tests:\n\n");
     CommandLineHelpContext context(&File::standardError(),
                                    eHelpOutputFormat_Console, NULL);
+    context.setModuleDisplayName(getProgramContext().displayName());
     CommandLineHelpWriter(options).writeHelp(context);
 }