Draft support for multiple help output formats.
authorTeemu Murtola <teemu.murtola@gmail.com>
Sun, 15 Jul 2012 09:51:07 +0000 (12:51 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Sun, 15 Jul 2012 13:48:48 +0000 (16:48 +0300)
- Added a HelpWriterContext class. An instance is now passed to all
  methods that write help text.  In addition to keeping track of the
  output format, it also allows other context information to be kept
  (e.g., for formatting reasonable cross-references; earlier I had to
  remove those that were in the selection help because static text was
  not appropriate for all situations).
- Moved current output format specific methods to be members of this
  class.
- Adjusted using methods to work as before for console output, but did
  not yet implement any additional output formats.  All methods with
  console-specific code now throw a NotImplementedError to make it easy
  to find them once #969 is decided.

Once there has been some discussion on the markup and output formats
in #969, this approach can be extended to actually implement it.
No matter which of the alternatives is chosen, the parts in this commit
are useful, but the decisions made in #969 affect the direction to which
the implementation should be extended.

Prerequisite for #969.

Change-Id: I33cd59e6f3b5450db99e0e1afba4a2d1b9e30e29

22 files changed:
src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/commandline/cmdlinehelpwriter.h
src/gromacs/commandline/cmdlinemodule.h
src/gromacs/commandline/cmdlinemodulemanager.cpp
src/gromacs/commandline/tests/cmdlinehelpwriter.cpp
src/gromacs/commandline/tests/cmdlinemodulemanager.cpp
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/onlinehelp/helpformat.h
src/gromacs/onlinehelp/helpmanager.cpp
src/gromacs/onlinehelp/helpmanager.h
src/gromacs/onlinehelp/helptopic.cpp
src/gromacs/onlinehelp/helptopic.h
src/gromacs/onlinehelp/helptopicinterface.h
src/gromacs/onlinehelp/helpwritercontext.cpp [new file with mode: 0644]
src/gromacs/onlinehelp/helpwritercontext.h [new file with mode: 0644]
src/gromacs/onlinehelp/tests/helpmanager.cpp
src/gromacs/selection/parsetree.cpp
src/gromacs/selection/selhelp.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.h
src/gromacs/trajectoryanalysis/modules.cpp
src/testutils/mock_helptopic.h

index a94533ee5db3d5a97dfd9a35b45e2a0e3faa8cb0..16c99caf45d51afff1f6f8e177c9a67e60af5a57 100644 (file)
@@ -40,6 +40,7 @@
 #include <string>
 
 #include "gromacs/onlinehelp/helpformat.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/options/basicoptioninfo.h"
 #include "gromacs/options/filenameoptioninfo.h"
 #include "gromacs/options/options.h"
@@ -47,6 +48,7 @@
 #include "gromacs/options/timeunitmanager.h"
 #include "gromacs/selection/selectionfileoptioninfo.h"
 #include "gromacs/selection/selectionoptioninfo.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -69,27 +71,31 @@ class DescriptionWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing section descriptions.
-        explicit DescriptionWriter(File *file) : file_(*file) {}
+        explicit DescriptionWriter(const HelpWriterContext &context)
+            : context_(context)
+        {
+        }
 
         virtual void visitSubSection(const Options &section);
         virtual void visitOption(const OptionInfo & /*option*/) { }
 
     private:
-        File                   &file_;
+        const HelpWriterContext &context_;
 };
 
 void DescriptionWriter::visitSubSection(const Options &section)
 {
     if (!section.description().empty())
     {
+        File &file = context_.outputFile();
         const std::string &title = section.title();
         if (!title.empty())
         {
-            file_.writeLine(title);
-            file_.writeLine();
+            file.writeLine(title);
+            file.writeLine();
         }
-        writeHelpTextForConsole(&file_, section.description());
-        file_.writeLine();
+        context_.writeTextBlock(section.description());
+        file.writeLine();
     }
     OptionsIterator(section).acceptSubSections(this);
 }
@@ -108,7 +114,7 @@ class FileParameterWriter : public OptionsTypeVisitor<FileNameOptionInfo>
 {
     public:
         //! Creates a helper object for writing file parameters.
-        explicit FileParameterWriter(File *file);
+        explicit FileParameterWriter(const HelpWriterContext &context);
 
         //! Returns true if anything was written out.
         bool didOutput() const { return formatter_.didOutput(); }
@@ -117,12 +123,12 @@ class FileParameterWriter : public OptionsTypeVisitor<FileNameOptionInfo>
         virtual void visitOptionType(const FileNameOptionInfo &option);
 
     private:
-        File                   &file_;
-        TextTableFormatter      formatter_;
+        const HelpWriterContext &context_;
+        TextTableFormatter       formatter_;
 };
 
-FileParameterWriter::FileParameterWriter(File *file)
-    : file_(*file)
+FileParameterWriter::FileParameterWriter(const HelpWriterContext &context)
+    : context_(context)
 {
     formatter_.addColumn("Option",      6, false);
     formatter_.addColumn("Filename",    12, false);
@@ -196,7 +202,7 @@ void FileParameterWriter::visitOptionType(const FileNameOptionInfo &option)
     }
     bool bLongType = (type.length() > 12U);
     formatter_.addColumnLine(2, type);
-    formatter_.addColumnLine(3, substituteMarkupForConsole(option.description()));
+    formatter_.addColumnLine(3, context_.substituteMarkup(option.description()));
 
     // Compute layout.
     if (name.length() > 6U || firstShortValue > 0)
@@ -222,7 +228,7 @@ void FileParameterWriter::visitOptionType(const FileNameOptionInfo &option)
     }
 
     // Do the formatting.
-    file_.writeString(formatter_.formatRow());
+    context_.outputFile().writeString(formatter_.formatRow());
 }
 
 
@@ -239,7 +245,8 @@ class ParameterWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing non-file parameters.
-        ParameterWriter(File *file, const char *timeUnit);
+        ParameterWriter(const HelpWriterContext &context,
+                        const char *timeUnit);
 
         //! Sets the writer to show hidden options.
         void setShowHidden(bool bSet) { bShowHidden_ = bSet; }
@@ -250,14 +257,15 @@ class ParameterWriter : public OptionsVisitor
         virtual void visitOption(const OptionInfo &option);
 
     private:
-        File                   &file_;
-        TextTableFormatter      formatter_;
-        const char             *timeUnit_;
-        bool                    bShowHidden_;
+        const HelpWriterContext &context_;
+        TextTableFormatter       formatter_;
+        const char              *timeUnit_;
+        bool                     bShowHidden_;
 };
 
-ParameterWriter::ParameterWriter(File *file, const char *timeUnit)
-    : file_(*file), timeUnit_(timeUnit), bShowHidden_(false)
+ParameterWriter::ParameterWriter(const HelpWriterContext &context,
+                                 const char *timeUnit)
+    : context_(context), timeUnit_(timeUnit), bShowHidden_(false)
 {
     formatter_.addColumn("Option",      12, false);
     formatter_.addColumn("Type",         6, false);
@@ -303,7 +311,7 @@ void ParameterWriter::visitOption(const OptionInfo &option)
         values.append(option.formatValue(i));
     }
     formatter_.addColumnLine(2, values);
-    std::string description(substituteMarkupForConsole(option.description()));
+    std::string description(context_.substituteMarkup(option.description()));
     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
     if (doubleOption != NULL && doubleOption->isTime())
     {
@@ -315,7 +323,7 @@ void ParameterWriter::visitOption(const OptionInfo &option)
         formatter_.setColumnFirstLineOffset(3, 1);
     }
 
-    file_.writeString(formatter_.formatRow());
+    context_.outputFile().writeString(formatter_.formatRow());
 }
 
 
@@ -332,7 +340,7 @@ class SelectionParameterWriter : public OptionsVisitor
 {
     public:
         //! Creates a helper object for writing selection parameters.
-        explicit SelectionParameterWriter(File *file);
+        explicit SelectionParameterWriter(const HelpWriterContext &context);
 
         //! Returns true if anything was written out.
         bool didOutput() const { return formatter_.didOutput(); }
@@ -341,12 +349,12 @@ class SelectionParameterWriter : public OptionsVisitor
         virtual void visitOption(const OptionInfo &option);
 
     private:
-        File                   &file_;
-        TextTableFormatter      formatter_;
+        const HelpWriterContext &context_;
+        TextTableFormatter       formatter_;
 };
 
-SelectionParameterWriter::SelectionParameterWriter(File *file)
-    : file_(*file)
+SelectionParameterWriter::SelectionParameterWriter(const HelpWriterContext &context)
+    : context_(context)
 {
     formatter_.addColumn("Selection",   10, false);
     formatter_.addColumn("Description", 67, true);
@@ -367,11 +375,13 @@ void SelectionParameterWriter::visitOption(const OptionInfo &option)
         return;
     }
 
+    File &file = context_.outputFile();
+
     formatter_.clear();
     std::string name(formatString("-%s", option.name().c_str()));
     formatter_.addColumnLine(0, name);
-    formatter_.addColumnLine(1, substituteMarkupForConsole(option.description()));
-    file_.writeString(formatter_.formatRow());
+    formatter_.addColumnLine(1, context_.substituteMarkup(option.description()));
+    file.writeString(formatter_.formatRow());
 
     // TODO: What to do with selection variables?
     // They are not printed as values for any option.
@@ -379,7 +389,7 @@ void SelectionParameterWriter::visitOption(const OptionInfo &option)
     {
         std::string value(option.formatValue(i));
         // TODO: Wrapping
-        file_.writeLine(formatString("    %s", value.c_str()));
+        file.writeLine(formatString("    %s", value.c_str()));
     }
 }
 
@@ -447,38 +457,46 @@ CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *time
     return *this;
 }
 
-void CommandLineHelpWriter::writeHelp(File *file)
+void CommandLineHelpWriter::writeHelp(const HelpWriterContext &context)
 {
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        // 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"));
+    }
+    File &file = context.outputFile();
     if (impl_->bShowDescriptions_)
     {
-        file->writeLine("DESCRIPTION");
-        file->writeLine("-----------");
-        file->writeLine();
-        DescriptionWriter(file).visitSubSection(impl_->options_);
+        file.writeLine("DESCRIPTION");
+        file.writeLine("-----------");
+        file.writeLine();
+        DescriptionWriter(context).visitSubSection(impl_->options_);
     }
     {
-        FileParameterWriter writer(file);
+        FileParameterWriter writer(context);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            file->writeLine();
+            file.writeLine();
         }
     }
     {
-        ParameterWriter writer(file, impl_->timeUnit_.c_str());
+        ParameterWriter writer(context, impl_->timeUnit_.c_str());
         writer.setShowHidden(impl_->bShowHidden_);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            file->writeLine();
+            file.writeLine();
         }
     }
     {
-        SelectionParameterWriter writer(file);
+        SelectionParameterWriter writer(context);
         writer.visitSubSection(impl_->options_);
         if (writer.didOutput())
         {
-            file->writeLine();
+            file.writeLine();
         }
     }
 }
index 7b01dc2b59fe4f23cca48f93a00fac7e92669bd1..5ff998669d350e74ccbf45fc6abb4dea285c7763 100644 (file)
@@ -44,7 +44,7 @@
 namespace gmx
 {
 
-class File;
+class HelpWriterContext;
 class Options;
 
 /*! \brief
@@ -87,9 +87,11 @@ class CommandLineHelpWriter
         /*! \brief
          * Writes the help.
          *
-         * \param[in] file  File to write the help to.
+         * \param[in] context  Context object for writing the help.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
          */
-        void writeHelp(File *file);
+        void writeHelp(const HelpWriterContext &context);
 
     private:
         class Impl;
index 63081773ee2ea0c9fc18130cb56a0d08c9420e24..d065f9c072130cf03e9de15d23fc500573efe559 100644 (file)
@@ -42,7 +42,7 @@
 namespace gmx
 {
 
-class File;
+class HelpWriterContext;
 
 /*! \brief
  * Module that can be run from command line using CommandLineModuleManager.
@@ -78,10 +78,11 @@ class CommandLineModuleInterface
         /*! \brief
          * Prints help for the module.
          *
-         * \param[in] file  File to write the help to.
+         * \param[in] context  Context object for writing the help.
          * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
          */
-        virtual void writeHelp(File *file) const = 0;
+        virtual void writeHelp(const HelpWriterContext &context) const = 0;
 };
 
 } // namespace gmx
index 6391619595406cd5d77be642d9197ceee253c5d1..0a72f49009ab1ef71219324d80573ab748a43e1b 100644 (file)
@@ -47,6 +47,7 @@
 #include "gromacs/onlinehelp/helpformat.h"
 #include "gromacs/onlinehelp/helpmanager.h"
 #include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
@@ -100,32 +101,47 @@ class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
         {
         }
 
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
     private:
-        void printModuleList(File *file) const;
+        void printModuleList(const HelpWriterContext &context) const;
 
         const CommandLineModuleMap &modules_;
 
         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
 };
 
-void RootHelpTopic::writeHelp(File *file) const
+void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    writeBasicHelpTopic(file, *this, helpText());
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        // TODO: Implement once the situation with Redmine issue #969 is more
+        // clear.
+        GMX_THROW(NotImplementedError(
+                    "Root help is not implemented for this output format"));
+    }
+    writeBasicHelpTopic(context, *this, helpText());
     // TODO: If/when this list becomes long, it may be better to only print
     // "common" commands here, and have a separate topic (e.g.,
     // "help commands") that prints the full list.
-    printModuleList(file);
-    writeHelpTextForConsole(file,
+    printModuleList(context);
+    context.writeTextBlock(
             "For additional help on a command, use '[PROGRAM] help <command>'");
-    writeSubTopicList(file, "\nAdditional help is available on the following topics:");
-    writeHelpTextForConsole(file,
+    writeSubTopicList(context,
+            "\nAdditional help is available on the following topics:");
+    context.writeTextBlock(
             "To access the help, use '[PROGRAM] help <topic>'.");
 }
 
-void RootHelpTopic::printModuleList(File *file) const
+void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
 {
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        // TODO: Implement once the situation with Redmine issue #969 is more
+        // clear.
+        GMX_THROW(NotImplementedError(
+                    "Module list is not implemented for this output format"));
+    }
     int maxNameLength = 0;
     CommandLineModuleMap::const_iterator module;
     for (module = modules_.begin(); module != modules_.end(); ++module)
@@ -136,12 +152,13 @@ void RootHelpTopic::printModuleList(File *file) const
             maxNameLength = nameLength;
         }
     }
+    File &file = context.outputFile();
     TextTableFormatter formatter;
     formatter.addColumn(NULL, maxNameLength + 1, false);
     formatter.addColumn(NULL, 72 - maxNameLength, true);
     formatter.setFirstColumnIndent(4);
-    file->writeLine();
-    file->writeLine("Available commands:");
+    file.writeLine();
+    file.writeLine("Available commands:");
     for (module = modules_.begin(); module != modules_.end(); ++module)
     {
         const char *name = module->first.c_str();
@@ -149,7 +166,7 @@ void RootHelpTopic::printModuleList(File *file) const
         formatter.clear();
         formatter.addColumnLine(0, name);
         formatter.addColumnLine(1, description);
-        file->writeString(formatter.formatRow());
+        file.writeString(formatter.formatRow());
     }
 }
 
@@ -182,7 +199,7 @@ class ModuleHelpTopic : public HelpTopicInterface
         {
             return NULL;
         }
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
     private:
         const CommandLineModuleInterface &module_;
@@ -190,9 +207,9 @@ class ModuleHelpTopic : public HelpTopicInterface
         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
 };
 
-void ModuleHelpTopic::writeHelp(File *file) const
+void ModuleHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    module_.writeHelp(file);
+    module_.writeHelp(context);
 }
 
 } // namespace
@@ -235,7 +252,7 @@ class CommandLineHelpModule : public CommandLineModuleInterface
         }
 
         virtual int run(int argc, char *argv[]);
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
         //! Prints usage message to stderr.
         void printUsage() const;
@@ -258,7 +275,9 @@ void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
 
 int CommandLineHelpModule::run(int argc, char *argv[])
 {
-    HelpManager helpManager(*rootTopic_);
+    HelpWriterContext context(&File::standardOutput(),
+                              eHelpOutputFormat_Console);
+    HelpManager helpManager(*rootTopic_, context);
     try
     {
         for (int i = 1; i < argc; ++i)
@@ -271,21 +290,23 @@ int CommandLineHelpModule::run(int argc, char *argv[])
         fprintf(stderr, "%s\n", ex.what());
         return 2;
     }
-    helpManager.writeCurrentTopic(&File::standardOutput());
+    helpManager.writeCurrentTopic();
     fprintf(stderr, "\n");
     return 0;
 }
 
-void CommandLineHelpModule::writeHelp(File *file) const
+void CommandLineHelpModule::writeHelp(const HelpWriterContext &context) const
 {
-    writeHelpTextForConsole(file,
+    context.writeTextBlock(
             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
     // TODO: More information.
 }
 
 void CommandLineHelpModule::printUsage() const
 {
-    rootTopic_->writeHelp(&File::standardError());
+    HelpWriterContext context(&File::standardError(),
+                              eHelpOutputFormat_Console);
+    rootTopic_->writeHelp(context);
 }
 
 /********************************************************************
@@ -338,11 +359,6 @@ class CommandLineModuleManager::Impl
         CommandLineModuleMap::const_iterator
         findModuleFromBinaryName(const ProgramInfo &programInfo) const;
 
-        //! Prints usage message to stderr.
-        void printUsage(bool bModuleList) const;
-        //! Prints the list of modules to stderr.
-        void printModuleList() const;
-
         /*! \brief
          * Maps module names to module objects.
          *
index ec531c0cb6e3d28e762760c0525e0b001b87663c..58a7b1c6bb75b5726886585c231dd6b6473bc9ad 100644 (file)
@@ -46,6 +46,7 @@
 #include "gromacs/legacyheaders/types/simple.h"
 
 #include "gromacs/commandline/cmdlinehelpwriter.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/options/basicoptions.h"
 #include "gromacs/options/filenameoption.h"
 #include "gromacs/options/options.h"
@@ -74,7 +75,8 @@ void CommandLineHelpWriterTest::checkHelp(gmx::CommandLineHelpWriter *writer)
 {
     std::string filename = tempFiles_.getTemporaryFilePath("helptext.txt");
     gmx::File file(filename, "w");
-    writer->writeHelp(&file);
+    gmx::HelpWriterContext context(&file, gmx::eHelpOutputFormat_Console);
+    writer->writeHelp(context);
     file.close();
 
     checkFileContents(filename, "HelpText");
index 436cba7359f2f26a9185862b5a569f06a3630a5f..658fc733978645817b28ed2792fa7f74278e1f01 100644 (file)
@@ -76,7 +76,7 @@ class MockModule : public gmx::CommandLineModuleInterface
         virtual const char *shortDescription() const { return descr_; }
 
         MOCK_METHOD2(run, int(int argc, char *argv[]));
-        MOCK_CONST_METHOD1(writeHelp, void(gmx::File *));
+        MOCK_CONST_METHOD1(writeHelp, void(const gmx::HelpWriterContext &context));
 
     private:
         const char             *name_;
index a746eeece8f8df51ada045ddec24550c846f28d9..61d8bb9447f5ec42adcb430103e675a190c91960 100644 (file)
  */
 #include "helpformat.h"
 
-#include <cctype>
-
 #include <algorithm>
 #include <string>
 #include <vector>
 
-#include "gromacs/legacyheaders/smalloc.h"
-#include "gromacs/legacyheaders/wman.h"
-
-#include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/programinfo.h"
 #include "gromacs/utility/stringutil.h"
 
 namespace gmx
 {
 
-/*! \cond libapi */
-std::string toUpperCase(const std::string &text)
-{
-    std::string result(text);
-    transform(result.begin(), result.end(), result.begin(), toupper);
-    return result;
-}
-
-std::string substituteMarkupForConsole(const std::string &text)
-{
-    char *resultStr = check_tty(text.c_str());
-    try
-    {
-        std::string result(resultStr);
-        sfree(resultStr);
-        return result;
-    }
-    catch (...)
-    {
-        sfree(resultStr);
-        throw;
-    }
-}
-
-void writeHelpTextForConsole(File *file, const std::string &text)
-{
-    TextLineWrapper wrapper;
-    wrapper.setLineLength(78);
-    const char *program = ProgramInfo::getInstance().programName().c_str();
-    std::string newText = replaceAll(text, "[PROGRAM]", program);
-    file->writeLine(wrapper.wrapToString(substituteMarkupForConsole(newText)));
-}
-//! \endcond
-
 /********************************************************************
  * TextTableFormatter::Impl
  */
index f91021335c97457ecdc6d1e6654e565e5a667dad..6af9cd084856c6f5af4d15c2a9aa07f2de5493e0 100644 (file)
@@ -48,44 +48,6 @@ namespace gmx
 
 class File;
 
-/*! \cond libapi */
-/*! \libinternal \brief
- * Make the string uppercase.
- *
- * \param[in] text  Input text.
- * \returns   \p text with all characters transformed to uppercase.
- * \throws    std::bad_alloc if out of memory.
- *
- * \inlibraryapi
- */
-std::string toUpperCase(const std::string &text);
-
-/*! \libinternal \brief
- * Substitute markup used in help text for console output.
- *
- * \param[in] text  Text to substitute.
- * \returns   \p text with markup substituted.
- * \throws    std::bad_alloc if out of memory.
- *
- * \inlibraryapi
- */
-std::string substituteMarkupForConsole(const std::string &text);
-/*! \libinternal \brief
- * Format a help text block for console output.
- *
- * \param     file  File to write the formatted text to.
- * \param[in] text  Text to format.
- * \throws    std::bad_alloc if out of memory.
- * \throws    FileIOError on any I/O error.
- *
- * Calls substituteMarkupForConsole(), and also wraps the lines to 78
- * characters.
- *
- * \inlibraryapi
- */
-void writeHelpTextForConsole(File *file, const std::string &text);
-//! \endcond
-
 /*! \libinternal \brief
  * Formats rows of a table for text output.
  *
index 224ba2f65c067093fc25f85ff9605b9c603f7db7..79f0afec708106f50c8b58b52f1b5b94aa089efc 100644 (file)
@@ -37,8 +37,6 @@
  */
 #include "helpmanager.h"
 
-#include <cstdio>
-
 #include <string>
 #include <vector>
 
@@ -64,6 +62,12 @@ class HelpManager::Impl
         //! Container type for keeping the stack of active topics.
         typedef std::vector<const HelpTopicInterface *> TopicStack;
 
+        //! Initializes a new manager with the given context.
+        explicit Impl(const HelpWriterContext &context)
+            : rootContext_(context)
+        {
+        }
+
         //! Whether the active topic is the root topic.
         bool isAtRootTopic() const { return topicStack_.size() == 1; }
         //! Returns the active topic.
@@ -74,13 +78,15 @@ class HelpManager::Impl
         //! Formats the active topic as a string, including its parent topics.
         std::string currentTopicAsString() const;
 
+        //! Context with which the manager was initialized.
+        const HelpWriterContext &rootContext_;
         /*! \brief
          * Stack of active topics.
          *
          * The first item is always the root topic, and each item is a subtopic
          * of the preceding item.  The last item is the currently active topic.
          */
-        TopicStack              topicStack_;
+        TopicStack               topicStack_;
 };
 
 std::string HelpManager::Impl::currentTopicAsString() const
@@ -102,8 +108,9 @@ std::string HelpManager::Impl::currentTopicAsString() const
  * HelpManager
  */
 
-HelpManager::HelpManager(const HelpTopicInterface &rootTopic)
-    : impl_(new Impl)
+HelpManager::HelpManager(const HelpTopicInterface &rootTopic,
+                         const HelpWriterContext &context)
+    : impl_(new Impl(context))
 {
     impl_->topicStack_.push_back(&rootTopic);
 }
@@ -139,10 +146,10 @@ void HelpManager::enterTopic(const char *name)
     impl_->topicStack_.push_back(newTopic);
 }
 
-void HelpManager::writeCurrentTopic(File *file) const
+void HelpManager::writeCurrentTopic() const
 {
     const HelpTopicInterface &topic = impl_->currentTopic();
-    topic.writeHelp(file);
+    topic.writeHelp(impl_->rootContext_);
 }
 
 } // namespace gmx
index 1b6bcab7f9b876a184834a066ed9996323bc205b..fc188100e73c643849ed250ef1589323c0027095 100644 (file)
@@ -44,8 +44,8 @@
 namespace gmx
 {
 
-class File;
 class HelpTopicInterface;
+class HelpWriterContext;
 
 /*! \libinternal \brief
  * Helper for providing interactive online help.
@@ -61,12 +61,14 @@ class HelpManager
          *
          * \param[in] rootTopic  Help topic that can be accessed through this
          *      manager.
+         * \param[in] context    Context object for writing the help.
          * \throws    std::bad_alloc if out of memory.
          *
-         * The provided topic must remain valid for the lifetime of this
-         * manager object.
+         * The provided topic and context objects must remain valid for the
+         * lifetime of this manager object.
          */
-        explicit HelpManager(const HelpTopicInterface &rootTopic);
+        HelpManager(const HelpTopicInterface &rootTopic,
+                    const HelpWriterContext &context);
         ~HelpManager();
 
         /*! \brief
@@ -81,11 +83,10 @@ class HelpManager
         /*! \brief
          * Writes out the help for the currently active topic.
          *
-         * \param   file  File to write the help text to.
          * \throws  std::bad_alloc if out of memory.
          * \throws  FileIOError on any I/O error.
          */
-        void writeCurrentTopic(File *file) const;
+        void writeCurrentTopic() const;
 
     private:
         class Impl;
index 3625b19b88632913ea26206e0606af88a36fa8f6..84ccc3f2f6b8254d6a29b1bf67d3d054e7483a72 100644 (file)
@@ -41,6 +41,7 @@
 #include <utility>
 
 #include "gromacs/onlinehelp/helpformat.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/gmxassert.h"
@@ -50,16 +51,16 @@ namespace gmx
 {
 
 /*! \cond libapi */
-void writeBasicHelpTopic(File *file, const HelpTopicInterface &topic,
+void writeBasicHelpTopic(const HelpWriterContext &context,
+                         const HelpTopicInterface &topic,
                          const std::string &text)
 {
     const char *title = topic.title();
     if (title != NULL && title[0] != '\0')
     {
-        file->writeLine(toUpperCase(title));
-        file->writeLine();
+        context.writeTitle(title);
     }
-    writeHelpTextForConsole(file, text);
+    context.writeTextBlock(text);
 }
 //! \endcond
 
@@ -78,9 +79,9 @@ AbstractSimpleHelpTopic::findSubTopic(const char *name) const
     return NULL;
 }
 
-void AbstractSimpleHelpTopic::writeHelp(File *file) const
+void AbstractSimpleHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    writeBasicHelpTopic(file, *this, helpText());
+    writeBasicHelpTopic(context, *this, helpText());
 }
 
 /********************************************************************
@@ -135,16 +136,23 @@ AbstractCompositeHelpTopic::findSubTopic(const char *name) const
     return topic->second.get();
 }
 
-void AbstractCompositeHelpTopic::writeHelp(File *file) const
+void AbstractCompositeHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    writeBasicHelpTopic(file, *this, helpText());
-    writeSubTopicList(file, "\nAvailable subtopics:");
+    writeBasicHelpTopic(context, *this, helpText());
+    writeSubTopicList(context, "\nAvailable subtopics:");
 }
 
 bool
-AbstractCompositeHelpTopic::writeSubTopicList(File *file,
+AbstractCompositeHelpTopic::writeSubTopicList(const HelpWriterContext &context,
                                               const std::string &title) const
 {
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        // TODO: Implement once the situation with Redmine issue #969 is more
+        // clear.
+        GMX_THROW(NotImplementedError(
+                    "Subtopic listing is not implemented for this output format"));
+    }
     int maxNameLength = 0;
     Impl::SubTopicMap::const_iterator topic;
     for (topic = impl_->subtopics_.begin(); topic != impl_->subtopics_.end(); ++topic)
@@ -164,11 +172,12 @@ AbstractCompositeHelpTopic::writeSubTopicList(File *file,
     {
         return false;
     }
+    File &file = context.outputFile();
     TextTableFormatter formatter;
     formatter.addColumn(NULL, maxNameLength + 1, false);
     formatter.addColumn(NULL, 72 - maxNameLength, true);
     formatter.setFirstColumnIndent(4);
-    file->writeLine(title);
+    file.writeLine(title);
     for (topic = impl_->subtopics_.begin(); topic != impl_->subtopics_.end(); ++topic)
     {
         const char *name = topic->first.c_str();
@@ -178,7 +187,7 @@ AbstractCompositeHelpTopic::writeSubTopicList(File *file,
             formatter.clear();
             formatter.addColumnLine(0, name);
             formatter.addColumnLine(1, title);
-            file->writeString(formatter.formatRow());
+            file.writeString(formatter.formatRow());
         }
     }
     return true;
index c2802da823d84c12a91f3c0935749b9d14f9f35e..5f1a5098b3786927cde43055556adbcd172fce0b 100644 (file)
@@ -52,16 +52,19 @@ namespace gmx
 /*! \libinternal \brief
  * Helper for writing simple help text.
  *
- * \param     file   File to write the help to.
- * \param[in] topic  Topic to write the help for (used for title).
- * \param[in] text   Text to write for the topic.
+ * \param[in] context Context for writing the help.
+ * \param[in] topic   Topic to write the help for (used for title).
+ * \param[in] text    Text to write for the topic.
+ * \throws    std::bad_alloc if out of memory.
+ * \throws    FileIOError on any I/O error.
  *
  * Formats basic help by writing a title (obtained from \p topic), followed by
  * \p text with markup substituted and lines properly wrapped.
  *
  * \inlibraryapi
  */
-void writeBasicHelpTopic(File *file, const HelpTopicInterface &topic,
+void writeBasicHelpTopic(const HelpWriterContext &context,
+                         const HelpTopicInterface &topic,
                          const std::string &text);
 //! \endcond
 
@@ -87,7 +90,7 @@ class AbstractSimpleHelpTopic : public HelpTopicInterface
         virtual bool hasSubTopics() const;
         virtual const HelpTopicInterface *findSubTopic(const char *name) const;
 
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
     protected:
         /*! \brief
@@ -127,7 +130,7 @@ class AbstractCompositeHelpTopic : public HelpTopicInterface
         virtual bool hasSubTopics() const;
         virtual const HelpTopicInterface *findSubTopic(const char *name) const;
 
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
         /*! \brief
          * Adds a given topic as a subtopic of this topic.
@@ -166,9 +169,11 @@ class AbstractCompositeHelpTopic : public HelpTopicInterface
         /*! \brief
          * Writes the list of subtopics.
          *
-         * \param     file   File to write the list to.
+         * \param[in] context Context for writing the help.
          * \param[in] title  Title for the written list.
          * \returns   true if anything was printed.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
          *
          * Subtopics with empty titles are skipped from the list.
          * If there would be no subtopics in the list, \p title is not printed
@@ -180,7 +185,8 @@ class AbstractCompositeHelpTopic : public HelpTopicInterface
          * subtopic list that is printed by the default writeHelp()
          * implementation.
          */
-        bool writeSubTopicList(File *file, const std::string &title) const;
+        bool writeSubTopicList(const HelpWriterContext &context,
+                               const std::string &title) const;
 
     private:
         class Impl;
index c1e71e6bd715f3d1e76730265e20b9a233e458f2..f4e35f8f0aeb597fdf180755a23918377ba9fd78 100644 (file)
@@ -44,7 +44,7 @@
 namespace gmx
 {
 
-class File;
+class HelpWriterContext;
 
 /*! \brief
  * Provides a single online help topic.
@@ -100,11 +100,11 @@ class HelpTopicInterface
         /*! \brief
          * Prints the help text for this topic.
          *
-         * \param   file  File to write the help text to.
-         * \throws  std::bad_alloc if out of memory.
-         * \throws  FileIOError on any I/O error.
+         * \param[in] context  Context object for writing the help.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
          */
-        virtual void writeHelp(File *file) const = 0;
+        virtual void writeHelp(const HelpWriterContext &context) const = 0;
 };
 
 //! Smart pointer type to manage a HelpTopicInterface object.
diff --git a/src/gromacs/onlinehelp/helpwritercontext.cpp b/src/gromacs/onlinehelp/helpwritercontext.cpp
new file mode 100644 (file)
index 0000000..aa32856
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \internal \file
+ * \brief
+ * Implements gmx::HelpWriterContext.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \ingroup module_onlinehelp
+ */
+#include "helpwritercontext.h"
+
+#include <cctype>
+
+#include <algorithm>
+
+#include "gromacs/legacyheaders/smalloc.h"
+#include "gromacs/legacyheaders/wman.h"
+
+#include "gromacs/onlinehelp/helpformat.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programinfo.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace
+{
+
+/*! \internal \brief
+ * Make the string uppercase.
+ *
+ * \param[in] text  Input text.
+ * \returns   \p text with all characters transformed to uppercase.
+ * \throws    std::bad_alloc if out of memory.
+ */
+std::string toUpperCase(const std::string &text)
+{
+    std::string result(text);
+    transform(result.begin(), result.end(), result.begin(), toupper);
+    return result;
+}
+
+} // namespace
+
+namespace gmx
+{
+
+/********************************************************************
+ * HelpWriterContext::Impl
+ */
+
+/*! \internal \brief
+ * Private implementation class for HelpWriterContext.
+ *
+ * \ingroup module_onlinehelp
+ */
+class HelpWriterContext::Impl
+{
+    public:
+        //! Initializes the context with the given output file and format.
+        explicit Impl(File *file, HelpOutputFormat format)
+            : file_(*file), format_(format)
+        {
+        }
+
+        //! Output file to which the help is written.
+        File                   &file_;
+        //! Output format for the help output.
+        HelpOutputFormat        format_;
+};
+
+/********************************************************************
+ * HelpWriterContext
+ */
+
+HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
+    : impl_(new Impl(file, format))
+{
+    if (format != eHelpOutputFormat_Console)
+    {
+        // TODO: Implement once the situation with Redmine issue #969 is more
+        // clear.
+        GMX_THROW(NotImplementedError(
+                    "This output format is not implemented"));
+    }
+}
+
+HelpWriterContext::~HelpWriterContext()
+{
+}
+
+HelpOutputFormat HelpWriterContext::outputFormat() const
+{
+    return impl_->format_;
+}
+
+File &HelpWriterContext::outputFile() const
+{
+    return impl_->file_;
+}
+
+std::string HelpWriterContext::substituteMarkup(const std::string &text) const
+{
+    char *resultStr = check_tty(text.c_str());
+    try
+    {
+        std::string result(resultStr);
+        sfree(resultStr);
+        return result;
+    }
+    catch (...)
+    {
+        sfree(resultStr);
+        throw;
+    }
+}
+
+void HelpWriterContext::writeTitle(const std::string &title) const
+{
+    File &file = outputFile();
+    file.writeLine(toUpperCase(title));
+    file.writeLine();
+}
+
+void HelpWriterContext::writeTextBlock(const std::string &text) const
+{
+    TextLineWrapper wrapper;
+    wrapper.setLineLength(78);
+    const char *program = ProgramInfo::getInstance().programName().c_str();
+    std::string newText = replaceAll(text, "[PROGRAM]", program);
+    outputFile().writeLine(wrapper.wrapToString(substituteMarkup(newText)));
+}
+
+} // namespace gmx
diff --git a/src/gromacs/onlinehelp/helpwritercontext.h b/src/gromacs/onlinehelp/helpwritercontext.h
new file mode 100644 (file)
index 0000000..095c2a5
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ *
+ *                This source code is part of
+ *
+ *                 G   R   O   M   A   C   S
+ *
+ *          GROningen MAchine for Chemical Simulations
+ *
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2009, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ *
+ * For more info, check our website at http://www.gromacs.org
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::HelpWriterContext.
+ *
+ * \author Teemu Murtola <teemu.murtola@cbr.su.se>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_HELPWRITERCONTEXT_H
+#define GMX_ONLINEHELP_HELPWRITERCONTEXT_H
+
+#include <string>
+
+#include "../utility/common.h"
+
+namespace gmx
+{
+
+class File;
+
+/*! \cond libapi */
+//! \libinternal Output format for help writing.
+enum HelpOutputFormat
+{
+    eHelpOutputFormat_Console,  //!< Plain text directly on the console.
+    eHelpOutputFormat_NR        //!< Used for the number of output formats.
+};
+//! \endcond
+
+/*! \libinternal \brief
+ * Context information for writing out help.
+ *
+ * The purpose of this class is to pass information about the output format to
+ * methods that write help, and to abstract away most of the details of
+ * different output formats.
+ * Additionally, it can keep other context information, although it currently
+ * does not.  Such additional context information would be useful for
+ * formatting links/references to other help topics.
+ *
+ * TODO: This class will need additional work as part of Redmine issue #969.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class HelpWriterContext
+{
+    public:
+        /*! \brief
+         * Initializes a context with the given output file and format.
+         *
+         * \throws std::bad_alloc if out of memory.
+         */
+        HelpWriterContext(File *file, HelpOutputFormat format);
+        ~HelpWriterContext();
+
+        /*! \brief
+         * Returns the active output format.
+         *
+         * Does not throw.
+         */
+        HelpOutputFormat outputFormat() const;
+        /*! \brief
+         * Returns the raw output file for writing the help.
+         *
+         * Using this file directly should be avoided, as it requires one to
+         * have different code for each output format.
+         * Using other methods in this class should be preferred.
+         *
+         * Does not throw.
+         */
+        File &outputFile() const;
+
+        /*! \brief
+         * Substitutes markup used in help text.
+         *
+         * \param[in] text  Text to substitute.
+         * \returns   \p text with markup substituted.
+         * \throws    std::bad_alloc if out of memory.
+         */
+        std::string substituteMarkup(const std::string &text) const;
+        /*! \brief
+         * Writes a title for the current help topic.
+         *
+         * \param[in] title  Title to write.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
+         */
+        void writeTitle(const std::string &title) const;
+        /*! \brief
+         * Writes a formatted text block into the output.
+         *
+         * \param[in] text  Text to format.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
+         *
+         * In addition to substituteMarkup(), also does line wrapping for
+         * console output.
+         *
+         * TODO: This function and substituteMarkup() should work more
+         * similarly.
+         */
+        void writeTextBlock(const std::string &text) const;
+
+    private:
+        class Impl;
+
+        PrivateImplPointer<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
index 57750e9ec391a2cde01b3a608ef3fa86e2266d89..34a049cfd6ccb3c4a7e8f11816cee62243a01d9c 100644 (file)
@@ -43,6 +43,7 @@
 #include <gtest/gtest.h>
 
 #include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 
@@ -62,12 +63,18 @@ class HelpTestBase : public gmx::test::StringTestBase
 
         gmx::test::TestFileManager tempFiles_;
         MockHelpTopic           rootTopic_;
+        std::string             filename_;
+        gmx::File               helpFile_;
+        gmx::HelpWriterContext  context_;
         gmx::HelpManager        manager_;
 };
 
 HelpTestBase::HelpTestBase()
     : rootTopic_("", NULL, "Root topic text"),
-      manager_(rootTopic_)
+      filename_(tempFiles_.getTemporaryFilePath("helptext.txt")),
+      helpFile_(filename_, "w"),
+      context_(&helpFile_, gmx::eHelpOutputFormat_Console),
+      manager_(rootTopic_, context_)
 {
 }
 
@@ -75,23 +82,13 @@ HelpTestBase::HelpTestBase()
  * Tests for HelpManager
  */
 
-class HelpManagerTest : public HelpTestBase
-{
-    public:
-        HelpManagerTest();
-
-        gmx::File               helpFile_;
-};
-
-HelpManagerTest::HelpManagerTest()
-    : helpFile_(tempFiles_.getTemporaryFilePath("helptext.txt"), "w")
-{
-}
+typedef HelpTestBase HelpManagerTest;
 
 TEST_F(HelpManagerTest, HandlesRootTopic)
 {
-    EXPECT_CALL(rootTopic_, writeHelp(&helpFile_));
-    manager_.writeCurrentTopic(&helpFile_);
+    using ::testing::_;
+    EXPECT_CALL(rootTopic_, writeHelp(_));
+    manager_.writeCurrentTopic();
 }
 
 TEST_F(HelpManagerTest, HandlesSubTopics)
@@ -102,10 +99,11 @@ TEST_F(HelpManagerTest, HandlesSubTopics)
         first.addSubTopic("firstsub", "First subtopic", "First subtopic text");
     rootTopic_.addSubTopic("second", "Second topic", "Second topic text");
 
-    EXPECT_CALL(firstSub, writeHelp(&helpFile_));
+    using ::testing::_;
+    EXPECT_CALL(firstSub, writeHelp(_));
     ASSERT_NO_THROW(manager_.enterTopic("first"));
     ASSERT_NO_THROW(manager_.enterTopic("firstsub"));
-    manager_.writeCurrentTopic(&helpFile_);
+    manager_.writeCurrentTopic();
 }
 
 TEST_F(HelpManagerTest, HandlesInvalidTopics)
@@ -148,13 +146,11 @@ class HelpTopicFormattingTest : public HelpTestBase
 
 void HelpTopicFormattingTest::checkHelpFormatting()
 {
-    std::string filename = tempFiles_.getTemporaryFilePath("helptext.txt");
     ASSERT_NO_THROW(manager_.enterTopic("testtopic"));
-    gmx::File file(filename, "w");
-    ASSERT_NO_THROW(manager_.writeCurrentTopic(&file));
-    file.close();
+    ASSERT_NO_THROW(manager_.writeCurrentTopic());
+    helpFile_.close();
 
-    checkFileContents(filename, "HelpText");
+    checkFileContents(filename_, "HelpText");
 }
 
 TEST_F(HelpTopicFormattingTest, FormatsSimpleTopic)
index 7d375b320c05cab1bdad53c4ea5c6ba92b9512e3..98b2d35b6e0de7d684f425bb2484d58a7bfa4592 100644 (file)
 #include "string2.h"
 
 #include "gromacs/onlinehelp/helpmanager.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/selection/poscalc.h"
 #include "gromacs/selection/selection.h"
 #include "gromacs/selection/selmethod.h"
@@ -1369,7 +1370,9 @@ _gmx_sel_handle_help_cmd(t_selexpr_value *topic, yyscan_t scanner)
     {
         sc->rootHelp = gmx::createSelectionHelpTopic();
     }
-    gmx::HelpManager manager(*sc->rootHelp);
+    gmx::HelpWriterContext context(&gmx::File::standardError(),
+                                   gmx::eHelpOutputFormat_Console);
+    gmx::HelpManager manager(*sc->rootHelp, context);
     try
     {
         t_selexpr_value *value = topic;
@@ -1384,5 +1387,5 @@ _gmx_sel_handle_help_cmd(t_selexpr_value *topic, yyscan_t scanner)
         fprintf(stderr, "%s\n", ex.what());
         return;
     }
-    manager.writeCurrentTopic(&gmx::File::standardError());
+    manager.writeCurrentTopic();
 }
index 87cc01321dce5617525e4bb8b95fc34ef4a23e7e..a22a9fad643805ae8515d7ab42cb82ab051db0d1 100644 (file)
@@ -42,6 +42,8 @@
 #include <boost/shared_ptr.hpp>
 
 #include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -459,7 +461,7 @@ class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
     public:
         KeywordsHelpTopic();
 
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
     private:
         /*! \brief
@@ -478,12 +480,13 @@ class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
         /*! \brief
          * Prints a brief list of keywords (selection methods) available.
          *
-         * \param[in] file  Where to write the list.
-         * \param[in] type  Only methods that return this type are printed.
+         * \param[in] context  Context for printing the help.
+         * \param[in] type     Only methods that return this type are printed.
          * \param[in] bModifiers  If false, \ref SMETH_MODIFIER methods are
          *      excluded, otherwise only them are printed.
          */
-        void printKeywordList(File *file, e_selvalue_t type, bool bModifiers) const;
+        void printKeywordList(const HelpWriterContext &context,
+                              e_selvalue_t type, bool bModifiers) const;
 
         MethodList              methods_;
 };
@@ -512,44 +515,59 @@ KeywordsHelpTopic::KeywordsHelpTopic()
     }
 }
 
-void KeywordsHelpTopic::writeHelp(File *file) const
+void KeywordsHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    writeBasicHelpTopic(file, *this, helpText());
-
-    /* Print the list of keywords */
-    file->writeLine();
-    file->writeLine("Keywords that select atoms by an integer property:");
-    file->writeLine("(use in expressions or like \"atomnr 1 to 5 7 9\")");
-    printKeywordList(file, INT_VALUE, false);
-
-    file->writeLine();
-    file->writeLine("Keywords that select atoms by a numeric property:");
-    file->writeLine("(use in expressions or like \"occupancy 0.5 to 1\")");
-    printKeywordList(file, REAL_VALUE, false);
-
-    file->writeLine();
-    file->writeLine("Keywords that select atoms by a string property:");
-    file->writeLine("(use like \"name PATTERN [PATTERN] ...\")");
-    printKeywordList(file, STR_VALUE, false);
-
-    file->writeLine();
-    file->writeLine("Additional keywords that directly select atoms:");
-    printKeywordList(file, GROUP_VALUE, false);
-
-    file->writeLine();
-    file->writeLine("Keywords that directly evaluate to positions:");
-    file->writeLine("(see also \"positions\" subtopic)");
-    printKeywordList(file, POS_VALUE, false);
-
-    file->writeLine();
-    file->writeLine("Additional keywords:");
-    printKeywordList(file, POS_VALUE, true);
-    printKeywordList(file, NO_VALUE, true);
+    if (context.outputFormat() != eHelpOutputFormat_Console)
+    {
+        GMX_THROW(NotImplementedError(
+                    "Selection help is not implemented for this output format"));
+    }
+    // TODO: The markup here is not really appropriate, and printKeywordList()
+    // still prints raw text, but these are waiting for discussion of the
+    // markup format in #969.
+    writeBasicHelpTopic(context, *this, helpText());
+    context.writeTextBlock("[BR]");
+
+    // Print the list of keywords
+    context.writeTextBlock(
+            "Keywords that select atoms by an integer property:[BR]"
+            "(use in expressions or like \"atomnr 1 to 5 7 9\")[BR]");
+    printKeywordList(context, INT_VALUE, false);
+    context.writeTextBlock("[BR]");
+
+    context.writeTextBlock(
+            "Keywords that select atoms by a numeric property:[BR]"
+            "(use in expressions or like \"occupancy 0.5 to 1\")[BR]");
+    printKeywordList(context, REAL_VALUE, false);
+    context.writeTextBlock("[BR]");
+
+    context.writeTextBlock(
+            "Keywords that select atoms by a string property:[BR]"
+            "(use like \"name PATTERN [PATTERN] ...\")[BR]");
+    printKeywordList(context, STR_VALUE, false);
+    context.writeTextBlock("[BR]");
+
+    context.writeTextBlock(
+            "Additional keywords that directly select atoms:[BR]");
+    printKeywordList(context, GROUP_VALUE, false);
+    context.writeTextBlock("[BR]");
+
+    context.writeTextBlock(
+            "Keywords that directly evaluate to positions:[BR]"
+            "(see also \"positions\" subtopic)[BR]");
+    printKeywordList(context, POS_VALUE, false);
+    context.writeTextBlock("[BR]");
+
+    context.writeTextBlock("Additional keywords:[BR]");
+    printKeywordList(context, POS_VALUE, true);
+    printKeywordList(context, NO_VALUE, true);
 }
 
-void KeywordsHelpTopic::printKeywordList(File *file, e_selvalue_t type,
+void KeywordsHelpTopic::printKeywordList(const HelpWriterContext &context,
+                                         e_selvalue_t type,
                                          bool bModifiers) const
 {
+    File &file = context.outputFile();
     MethodList::const_iterator iter;
     for (iter = methods_.begin(); iter != methods_.end(); ++iter)
     {
@@ -558,20 +576,19 @@ void KeywordsHelpTopic::printKeywordList(File *file, e_selvalue_t type,
         if (method.type == type && bModifiers == bIsModifier)
         {
             bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
-            file->writeString(formatString(" %c ", bHasHelp ? '*' : ' '));
+            file.writeString(formatString(" %c ", bHasHelp ? '*' : ' '));
             if (method.help.syntax != NULL)
             {
-                file->writeLine(method.help.syntax);
+                file.writeLine(method.help.syntax);
             }
             else
             {
-                const std::string &symname = iter->first;
-                file->writeString(symname);
+                std::string symname = iter->first;
                 if (symname != method.name)
                 {
-                    file->writeString(formatString(" (synonym for %s)", method.name));
+                    symname.append(formatString(" (synonym for %s)", method.name));
                 }
-                file->writeLine();
+                file.writeLine(symname);
             }
         }
     }
index 42566a1fc7fbd1e985de4b0cd9ad2ea963e0f68e..ac6eb98df8abb4391a36b30bf8517e7195f5920d 100644 (file)
@@ -47,6 +47,7 @@
 #include "gromacs/analysisdata/paralleloptions.h"
 #include "gromacs/commandline/cmdlinehelpwriter.h"
 #include "gromacs/commandline/cmdlineparser.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/options/options.h"
 #include "gromacs/selection/selectioncollection.h"
 #include "gromacs/selection/selectionoptioninfo.h"
@@ -107,11 +108,13 @@ TrajectoryAnalysisCommandLineRunner::Impl::printHelp(
     TrajectoryAnalysisRunnerCommon::HelpFlags flags = common.helpFlags();
     if (flags != 0)
     {
+        HelpWriterContext context(&File::standardError(),
+                                  eHelpOutputFormat_Console);
         CommandLineHelpWriter(options)
             .setShowDescriptions(flags & TrajectoryAnalysisRunnerCommon::efHelpShowDescriptions)
             .setShowHidden(flags & TrajectoryAnalysisRunnerCommon::efHelpShowHidden)
             .setTimeUnitString(settings.timeUnitManager().timeUnitAsString())
-            .writeHelp(&File::standardError());
+            .writeHelp(context);
     }
 }
 
@@ -282,7 +285,7 @@ TrajectoryAnalysisCommandLineRunner::run(int argc, char *argv[])
 
 
 void
-TrajectoryAnalysisCommandLineRunner::writeHelp(File *file)
+TrajectoryAnalysisCommandLineRunner::writeHelp(const HelpWriterContext &context)
 {
     // TODO: This method duplicates some code from run() and Impl::printHelp().
     // See how to best refactor it to share the common code.
@@ -309,7 +312,7 @@ TrajectoryAnalysisCommandLineRunner::writeHelp(File *file)
     CommandLineHelpWriter(options)
         .setShowDescriptions(true)
         .setTimeUnitString(settings.timeUnitManager().timeUnitAsString())
-        .writeHelp(file);
+        .writeHelp(context);
 }
 
 } // namespace gmx
index 78150927092043076c3d40facfa66b98520f6348..0558e9cf71653567af0e9d0e3c95e7e6ce93ec46 100644 (file)
@@ -44,7 +44,7 @@
 namespace gmx
 {
 
-class File;
+class HelpWriterContext;
 class TrajectoryAnalysisModule;
 
 /*! \brief
@@ -106,10 +106,11 @@ class TrajectoryAnalysisCommandLineRunner
         /*! \brief
          * Prints help for the module, including common options from the runner.
          *
-         * \param[in] file  File to write the help to.
+         * \param[in] context  Context object for writing the help.
          * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
          */
-        void writeHelp(File *file);
+        void writeHelp(const HelpWriterContext &context);
 
     private:
         class Impl;
index c4e8cb0c82836964dea8f233d7f0cc0bf54b783a..096fc398acf3880e82ec639f470397ca5ac2d50f 100644 (file)
@@ -66,7 +66,7 @@ class AbstractTrajAnalysisCmdLineWrapper : public CommandLineModuleInterface
         virtual const char *shortDescription() const = 0;
 
         virtual int run(int argc, char *argv[]);
-        virtual void writeHelp(File *file) const;
+        virtual void writeHelp(const HelpWriterContext &context) const;
 
     protected:
         //! Creates the analysis module for this wrapper.
@@ -81,11 +81,11 @@ int AbstractTrajAnalysisCmdLineWrapper::run(int argc, char *argv[])
     return runner.run(argc, argv);
 }
 
-void AbstractTrajAnalysisCmdLineWrapper::writeHelp(File *file) const
+void AbstractTrajAnalysisCmdLineWrapper::writeHelp(const HelpWriterContext &context) const
 {
     TrajectoryAnalysisModulePointer module(createModule());
     TrajectoryAnalysisCommandLineRunner runner(module.get());
-    runner.writeHelp(file);
+    runner.writeHelp(context);
 }
 
 /*! \internal \brief
index 12ddf461f55c56016669b2d8d1a53c5a0596be21..fe4d455e3f02db9a6948e093d0b72f589121f1d8 100644 (file)
@@ -42,6 +42,7 @@
 #include <gmock/gmock.h>
 
 #include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
 
 namespace gmx
 {
@@ -61,7 +62,7 @@ class MockHelpTopic : public AbstractCompositeHelpTopic
         virtual const char *name() const;
         virtual const char *title() const;
 
-        MOCK_CONST_METHOD1(writeHelp, void(File *));
+        MOCK_CONST_METHOD1(writeHelp, void(const HelpWriterContext &context));
 
         MockHelpTopic &addSubTopic(const char *name, const char *title,
                                    const char *text);