Improve 'g_ana help'.
authorTeemu Murtola <teemu.murtola@gmail.com>
Wed, 16 May 2012 06:51:50 +0000 (09:51 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Tue, 29 May 2012 13:56:54 +0000 (16:56 +0300)
- 'g_ana help' now internally uses the new online help classes.
  Without any additional arguments, it still prints out the list of
  commands, and now also list of additional help topics.
- 'g_ana help <command>' prints help for the given command (the same as
  'g_ana <command> -h' for the trajectory analysis modules).
- 'g_ana help selections <subtopic>' can be used to access the general
  help for selections.

IssueID #666.

Change-Id: Id34044842b297591923bef242a93b4ae1ae9e14e

13 files changed:
src/gromacs/commandline/cmdlinemodule.h
src/gromacs/commandline/cmdlinemodulemanager.cpp
src/gromacs/commandline/cmdlinemodulemanager.h
src/gromacs/commandline/tests/cmdlinemodulemanager.cpp
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/selection/selectioncollection.cpp
src/gromacs/selection/selectioncollection.h
src/gromacs/trajectoryanalysis/cmdlinerunner.cpp
src/gromacs/trajectoryanalysis/cmdlinerunner.h
src/gromacs/trajectoryanalysis/modules.cpp
src/gromacs/utility/file.cpp
src/gromacs/utility/file.h
src/programs/g_ana/g_ana.cpp

index b60a462bc6d3b1024d40d1d16453e5a76a5a572c..63081773ee2ea0c9fc18130cb56a0d08c9420e24 100644 (file)
@@ -42,6 +42,8 @@
 namespace gmx
 {
 
+class File;
+
 /*! \brief
  * Module that can be run from command line using CommandLineModuleManager.
  *
@@ -73,6 +75,13 @@ class CommandLineModuleInterface
          * the module was run as a standalone executable.
          */
         virtual int run(int argc, char *argv[]) = 0;
+        /*! \brief
+         * Prints help for the module.
+         *
+         * \param[in] file  File to write the help to.
+         * \throws    std::bad_alloc if out of memory.
+         */
+        virtual void writeHelp(File *file) const = 0;
 };
 
 } // namespace gmx
index 41b7e1640416a6694778a7c3bc5c2041706bb1a3..e7962b4782399b8e1525d0d0392741700b1a8659 100644 (file)
 
 #include "gromacs/commandline/cmdlinemodule.h"
 #include "gromacs/onlinehelp/helpformat.h"
+#include "gromacs/onlinehelp/helpmanager.h"
+#include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/utility/file.h"
+#include "gromacs/utility/format.h"
+#include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/programinfo.h"
 
 namespace gmx
 {
 
+//! Container type for mapping module names to module objects.
+typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
+
+namespace
+{
+
 /********************************************************************
- * CommandLineModuleManager::Impl
+ * RootHelpTopic
  */
 
+struct RootHelpText
+{
+    static const char name[];
+    static const char title[];
+    static const char *const text[];
+};
+
+// The first two are not used.
+const char RootHelpText::name[] = "";
+const char RootHelpText::title[] = "";
+const char *const RootHelpText::text[] = {
+    "Usage: [PROGRAM] <command> [<args>]",
+};
+
 /*! \internal \brief
- * Private implementation class for CommandLineModuleManager.
+ * Help topic that forms the root of the help tree for the help subcommand.
  *
  * \ingroup module_commandline
  */
-class CommandLineModuleManager::Impl
+class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
 {
     public:
-        //! Container for mapping module names to module objects.
-        typedef std::map<std::string, CommandLineModulePointer> ModuleMap;
-
-        /*! \brief
-         * Initializes the implementation class.
-         *
-         * \param[in] programInfo  Program information for the running binary.
-         */
-        explicit Impl(const ProgramInfo &programInfo);
-
         /*! \brief
-         * Finds a module that matches a name.
+         * Creates a root help topic.
          *
-         * \param[in] name  Module name to find.
-         * \returns   Iterator to the found module, or
-         *      \c modules_.end() if not found.
+         * \param[in] modules  List of modules for to use for module listings.
          *
          * Does not throw.
          */
-        ModuleMap::const_iterator findModuleByName(const std::string &name) const;
-        /*! \brief
-         * Finds a module that the name of the binary.
-         *
-         * \param[in] programInfo  Program information object to use.
-         * \throws    std::bad_alloc if out of memory.
-         * \returns   Iterator to the found module, or
-         *      \c modules_.end() if not found.
-         *
-         * Checks whether the program is invoked through a symlink whose name
-         * is different from ProgramInfo::realBinaryName(), and if so, checks
-         * if a module name matches the name of the symlink.
-         *
-         * Note that the \p programInfo parameter is currently not necessary
-         * (as the program info object is also contained as a member), but it
-         * clarifies the control flow.
-         */
-        ModuleMap::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;
+        explicit RootHelpTopic(const CommandLineModuleMap &modules)
+            : modules_(modules)
+        {
+        }
 
-        /*! \brief
-         * Maps module names to module objects.
-         *
-         * Owns the contained modules.
-         */
-        ModuleMap               modules_;
-        //! Information about the currently running program.
-        const ProgramInfo      &programInfo_;
-};
+        virtual void writeHelp(File *file) const;
 
-CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
-    : programInfo_(programInfo)
-{
-}
+    private:
+        void printModuleList(File *file) const;
 
-CommandLineModuleManager::Impl::ModuleMap::const_iterator
-CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
-{
-    // TODO: Accept unambiguous prefixes?
-    return modules_.find(name);
-}
+        const CommandLineModuleMap &modules_;
 
-CommandLineModuleManager::Impl::ModuleMap::const_iterator
-CommandLineModuleManager::Impl::findModuleFromBinaryName(
-        const ProgramInfo &programInfo) const
-{
-    std::string binaryName = programInfo.invariantProgramName();
-    if (binaryName == programInfo.realBinaryName())
-    {
-        return modules_.end();
-    }
-    if (binaryName.compare(0, 2, "g_") == 0)
-    {
-        binaryName.erase(0, 2);
-    }
-    return findModuleByName(binaryName);
-}
+        GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
+};
 
-void CommandLineModuleManager::Impl::printUsage(bool bModuleList) const
+void RootHelpTopic::writeHelp(File *file) const
 {
-    const char *program = programInfo_.programName().c_str();
-    fprintf(stderr, "Usage: %s <command> [<args>]\n\n", program);
-    if (bModuleList)
-    {
-        printModuleList();
-    }
-    else
-    {
-        fprintf(stderr, "See '%s help' for list of commands.\n", program);
-    }
+    writeBasicHelpTopic(file, *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,
+            "For additional help on a command, use '[PROGRAM] help <command>'");
+    writeSubTopicList(file, "\nAdditional help is available on the following topics:");
+    writeHelpTextForConsole(file,
+            "To access the help, use '[PROGRAM] help <topic>'.");
 }
 
-void CommandLineModuleManager::Impl::printModuleList() const
+void RootHelpTopic::printModuleList(File *file) const
 {
     int maxNameLength = 0;
-    ModuleMap::const_iterator module;
+    CommandLineModuleMap::const_iterator module;
     for (module = modules_.begin(); module != modules_.end(); ++module)
     {
         int nameLength = static_cast<int>(module->first.length());
@@ -175,7 +140,8 @@ void CommandLineModuleManager::Impl::printModuleList() const
     formatter.addColumn(NULL, maxNameLength + 1, false);
     formatter.addColumn(NULL, 72 - maxNameLength, true);
     formatter.setFirstColumnIndent(4);
-    fprintf(stderr, "The following commands are available:\n");
+    file->writeLine();
+    file->writeLine("Available commands:");
     for (module = modules_.begin(); module != modules_.end(); ++module)
     {
         const char *name = module->first.c_str();
@@ -183,17 +149,57 @@ void CommandLineModuleManager::Impl::printModuleList() const
         formatter.clear();
         formatter.addColumnLine(0, name);
         formatter.addColumnLine(1, description);
-        fprintf(stderr, "%s", formatter.formatRow().c_str());
+        file->writeString(formatter.formatRow());
     }
 }
 
-
 /********************************************************************
- * CommandLineHelpModule
+ * ModuleHelpTopic
+ */
+
+/*! \internal \brief
+ * Help topic wrapper for a command-line module.
+ *
+ * This class implements HelpTopicInterface such that it wraps a
+ * CommandLineModuleInterface, allowing subcommand "help <command>"
+ * to produce the help for "<command>".
+ *
+ * \ingroup module_commandline
  */
+class ModuleHelpTopic : public HelpTopicInterface
+{
+    public:
+        //! Constructs a help topic for a specific module.
+        explicit ModuleHelpTopic(const CommandLineModuleInterface &module)
+            : module_(module)
+        {
+        }
+
+        virtual const char *name() const { return module_.name(); }
+        virtual const char *title() const { return NULL; }
+        virtual bool hasSubTopics() const { return false; }
+        virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
+        {
+            return NULL;
+        }
+        virtual void writeHelp(File *file) const;
+
+    private:
+        const CommandLineModuleInterface &module_;
 
-namespace internal
+        GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
+};
+
+void ModuleHelpTopic::writeHelp(File *file) const
 {
+    module_.writeHelp(file);
+}
+
+} // namespace
+
+/********************************************************************
+ * CommandLineHelpModule
+ */
 
 /*! \internal \brief
  * Command-line module for producing help.
@@ -207,13 +213,20 @@ class CommandLineHelpModule : public CommandLineModuleInterface
 {
     public:
         /*! \brief
-         * Creates a help module for the given module manager.
+         * Creates a command-line help module.
          *
-         * \param[in] manager  Manager for which this module provides help.
+         * \param[in] modules  List of modules for to use for module listings.
+         * \throws    std::bad_alloc if out of memory.
+         */
+        explicit CommandLineHelpModule(const CommandLineModuleMap &modules);
+
+        /*! \brief
+         * Adds a top-level help topic.
          *
-         * Does not throw.
+         * \param[in] topic  Help topic to add.
+         * \throws    std::bad_alloc if out of memory.
          */
-        explicit CommandLineHelpModule(const CommandLineModuleManager &manager);
+        void addTopic(HelpTopicPointer topic);
 
         virtual const char *name() const { return "help"; }
         virtual const char *shortDescription() const
@@ -222,26 +235,157 @@ class CommandLineHelpModule : public CommandLineModuleInterface
         }
 
         virtual int run(int argc, char *argv[]);
+        virtual void writeHelp(File *file) const;
+
+        //! Prints usage message to stderr.
+        void printUsage() const;
 
     private:
-        const CommandLineModuleManager &manager_;
+        CompositeHelpTopicPointer   rootTopic_;
 
         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
 };
 
-CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleManager &manager)
-    : manager_(manager)
+CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleMap &modules)
+    : rootTopic_(new RootHelpTopic(modules))
+{
+}
+
+void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
 {
+    rootTopic_->addSubTopic(move(topic));
 }
 
 int CommandLineHelpModule::run(int argc, char *argv[])
 {
-    manager_.impl_->printUsage(true);
+    HelpManager helpManager(*rootTopic_);
+    try
+    {
+        for (int i = 1; i < argc; ++i)
+        {
+            helpManager.enterTopic(argv[i]);
+        }
+    }
+    catch (const InvalidInputError &ex)
+    {
+        fprintf(stderr, "%s\n", ex.what());
+        return 2;
+    }
+    helpManager.writeCurrentTopic(&File::standardOutput());
+    fprintf(stderr, "\n");
     return 0;
 }
 
-} // namespace internal
+void CommandLineHelpModule::writeHelp(File *file) const
+{
+    writeHelpTextForConsole(file,
+            "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
+    // TODO: More information.
+}
+
+void CommandLineHelpModule::printUsage() const
+{
+    rootTopic_->writeHelp(&File::standardError());
+}
+
+/********************************************************************
+ * CommandLineModuleManager::Impl
+ */
+
+/*! \internal \brief
+ * Private implementation class for CommandLineModuleManager.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineModuleManager::Impl
+{
+    public:
+
+        /*! \brief
+         * Initializes the implementation class.
+         *
+         * \param[in] programInfo  Program information for the running binary.
+         */
+        explicit Impl(const ProgramInfo &programInfo);
+
+        /*! \brief
+         * Finds a module that matches a name.
+         *
+         * \param[in] name  Module name to find.
+         * \returns   Iterator to the found module, or
+         *      \c modules_.end() if not found.
+         *
+         * Does not throw.
+         */
+        CommandLineModuleMap::const_iterator
+        findModuleByName(const std::string &name) const;
+        /*! \brief
+         * Finds a module that the name of the binary.
+         *
+         * \param[in] programInfo  Program information object to use.
+         * \throws    std::bad_alloc if out of memory.
+         * \returns   Iterator to the found module, or
+         *      \c modules_.end() if not found.
+         *
+         * Checks whether the program is invoked through a symlink whose name
+         * is different from ProgramInfo::realBinaryName(), and if so, checks
+         * if a module name matches the name of the symlink.
+         *
+         * Note that the \p programInfo parameter is currently not necessary
+         * (as the program info object is also contained as a member), but it
+         * clarifies the control flow.
+         */
+        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.
+         *
+         * Owns the contained modules.
+         */
+        CommandLineModuleMap    modules_;
+        //! Information about the currently running program.
+        const ProgramInfo      &programInfo_;
+        /*! \brief
+         * Module that implements help for the binary.
+         *
+         * The pointed module is owned by the \a modules_ container.
+         */
+        CommandLineHelpModule  *helpModule_;
+};
+
+CommandLineModuleManager::Impl::Impl(const ProgramInfo &programInfo)
+    : programInfo_(programInfo)
+{
+}
+
+CommandLineModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleByName(const std::string &name) const
+{
+    // TODO: Accept unambiguous prefixes?
+    return modules_.find(name);
+}
 
+CommandLineModuleMap::const_iterator
+CommandLineModuleManager::Impl::findModuleFromBinaryName(
+        const ProgramInfo &programInfo) const
+{
+    std::string binaryName = programInfo.invariantProgramName();
+    if (binaryName == programInfo.realBinaryName())
+    {
+        return modules_.end();
+    }
+    if (binaryName.compare(0, 2, "g_") == 0)
+    {
+        binaryName.erase(0, 2);
+    }
+    return findModuleByName(binaryName);
+}
 
 /********************************************************************
  * CommandLineModuleManager
@@ -250,7 +394,8 @@ int CommandLineHelpModule::run(int argc, char *argv[])
 CommandLineModuleManager::CommandLineModuleManager(const ProgramInfo &programInfo)
     : impl_(new Impl(programInfo))
 {
-    addModule(CommandLineModulePointer(new internal::CommandLineHelpModule(*this)));
+    impl_->helpModule_ = new CommandLineHelpModule(impl_->modules_);
+    addModule(CommandLineModulePointer(impl_->helpModule_));
 }
 
 CommandLineModuleManager::~CommandLineModuleManager()
@@ -261,21 +406,27 @@ void CommandLineModuleManager::addModule(CommandLineModulePointer module)
 {
     GMX_ASSERT(impl_->modules_.find(module->name()) == impl_->modules_.end(),
                "Attempted to register a duplicate module name");
+    HelpTopicPointer helpTopic(new ModuleHelpTopic(*module));
     impl_->modules_.insert(std::make_pair(std::string(module->name()),
                                           move(module)));
+    addHelpTopic(move(helpTopic));
+}
+
+void CommandLineModuleManager::addHelpTopic(HelpTopicPointer topic)
+{
+    impl_->helpModule_->addTopic(move(topic));
 }
 
 int CommandLineModuleManager::run(int argc, char *argv[])
 {
     int argOffset = 0;
-    Impl::ModuleMap::const_iterator module
+    CommandLineModuleMap::const_iterator module
         = impl_->findModuleFromBinaryName(impl_->programInfo_);
     if (module == impl_->modules_.end())
     {
         if (argc < 2)
         {
-            fprintf(stderr, "\n");
-            impl_->printUsage(false);
+            impl_->helpModule_->printUsage();
             return 2;
         }
         module = impl_->findModuleByName(argv[1]);
@@ -283,9 +434,8 @@ int CommandLineModuleManager::run(int argc, char *argv[])
     }
     if (module == impl_->modules_.end())
     {
-        fprintf(stderr, "\n");
-        fprintf(stderr, "Unknown command: '%s'\n", argv[1]);
-        impl_->printUsage(true);
+        fprintf(stderr, "Unknown command: '%s'\n\n", argv[1]);
+        impl_->helpModule_->printUsage();
         return 2;
     }
     return module->second->run(argc - argOffset, argv + argOffset);
index 050946f737f4e4d6343b432c9ce3a3236fc0500c..a665488f426fad20df52ff0da12ecbaabb66d2f5 100644 (file)
@@ -39,6 +39,7 @@
 #ifndef GMX_COMMANDLINE_CMDLINEMODULEMANAGER_H
 #define GMX_COMMANDLINE_CMDLINEMODULEMANAGER_H
 
+#include "../onlinehelp/helptopicinterface.h"
 #include "../utility/common.h"
 #include "../utility/uniqueptr.h"
 
@@ -52,11 +53,6 @@ class ProgramInfo;
 typedef gmx_unique_ptr<CommandLineModuleInterface>::type
         CommandLineModulePointer;
 
-namespace internal
-{
-class CommandLineHelpModule;
-}
-
 /*! \brief
  * Implements a wrapper command-line interface for multiple modules.
  *
@@ -134,6 +130,16 @@ class CommandLineModuleManager
             addModule(CommandLineModulePointer(new Module));
         }
 
+        /*! \brief
+         * Make given help topic available through the manager's help module.
+         *
+         * \param[in]  topic  Help topic to add.
+         * \throws     std::bad_alloc if out of memory.
+         *
+         * The manager takes ownership of the help topic.
+         */
+        void addHelpTopic(HelpTopicPointer topic);
+
         /*! \brief
          * Runs a module based on given command line.
          *
@@ -153,11 +159,6 @@ class CommandLineModuleManager
         class Impl;
 
         PrivateImplPointer<Impl> impl_;
-
-        /*! \brief
-         * Needed to access information about registered modules etc.
-         */
-        friend class internal::CommandLineHelpModule;
 };
 
 } // namespace gmx
index 08adc34dc112678e10b9054689dc86cda4ca77bf..436cba7359f2f26a9185862b5a569f06a3630a5f 100644 (file)
 #include "gromacs/utility/programinfo.h"
 
 #include "testutils/cmdlinetest.h"
+#include "testutils/mock_helptopic.h"
 
 namespace
 {
 
 using gmx::test::CommandLine;
+using gmx::test::MockHelpTopic;
 
 /********************************************************************
  * MockModule
@@ -74,6 +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 *));
 
     private:
         const char             *name_;
@@ -94,6 +97,7 @@ class CommandLineModuleManagerTest : public ::testing::Test
     public:
         void initManager(const CommandLine &args);
         MockModule &addModule(const char *name, const char *description);
+        MockHelpTopic &addHelpTopic(const char *name, const char *title);
 
         gmx::CommandLineModuleManager &manager() { return *manager_; }
 
@@ -117,6 +121,14 @@ CommandLineModuleManagerTest::addModule(const char *name, const char *descriptio
     return *module;
 }
 
+MockHelpTopic &
+CommandLineModuleManagerTest::addHelpTopic(const char *name, const char *title)
+{
+    MockHelpTopic *topic = new MockHelpTopic(name, title, "Help text");
+    manager().addHelpTopic(gmx::HelpTopicPointer(topic));
+    return *topic;
+}
+
 /********************************************************************
  * Actual tests
  */
@@ -140,6 +152,38 @@ TEST_F(CommandLineModuleManagerTest, RunsModule)
     ASSERT_EQ(0, rc);
 }
 
+TEST_F(CommandLineModuleManagerTest, RunsModuleHelp)
+{
+    const char *const cmdline[] = {
+        "g_test", "help", "module"
+    };
+    CommandLine args(CommandLine::create(cmdline));
+    initManager(args);
+    MockModule &mod1 = addModule("module", "First module");
+    addModule("other", "Second module");
+    using ::testing::_;
+    EXPECT_CALL(mod1, writeHelp(_));
+    int rc = 0;
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
+    ASSERT_EQ(0, rc);
+}
+
+TEST_F(CommandLineModuleManagerTest, PrintsHelpOnTopic)
+{
+    const char *const cmdline[] = {
+        "g_test", "help", "topic"
+    };
+    CommandLine args(CommandLine::create(cmdline));
+    initManager(args);
+    addModule("module", "First module");
+    MockHelpTopic &topic = addHelpTopic("topic", "Test topic");
+    using ::testing::_;
+    EXPECT_CALL(topic, writeHelp(_));
+    int rc = 0;
+    ASSERT_NO_THROW(rc = manager().run(args.argc(), args.argv()));
+    ASSERT_EQ(0, rc);
+}
+
 TEST_F(CommandLineModuleManagerTest, RunsModuleBasedOnBinaryName)
 {
     const char *const cmdline[] = {
index af027e68ca4fa0737208777bee3310fc4001a3f6..257171dd13c4b5ed9e957efa65895e4fd3ed8620 100644 (file)
@@ -49,6 +49,7 @@
 #include "gromacs/utility/file.h"
 #include "gromacs/utility/format.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/programinfo.h"
 
 namespace gmx
 {
@@ -81,7 +82,9 @@ void writeHelpTextForConsole(File *file, const std::string &text)
 {
     TextLineWrapper wrapper;
     wrapper.setLineLength(78);
-    file->writeLine(wrapper.wrapToString(substituteMarkupForConsole(text)));
+    const char *program = ProgramInfo::getInstance().programName().c_str();
+    std::string newText = replaceAll(text, "[PROGRAM]", program);
+    file->writeLine(wrapper.wrapToString(substituteMarkupForConsole(newText)));
 }
 //! \endcond
 
index 9314e072a959ad8d7574926201644d740f8a3f8c..61ca1fec9e11c2f5b204b720871492a70b71123d 100644 (file)
@@ -60,6 +60,7 @@
 #include "scanner.h"
 #include "selectioncollection-impl.h"
 #include "selelem.h"
+#include "selhelp.h"
 #include "selmethod.h"
 #include "symrec.h"
 
@@ -513,4 +514,11 @@ SelectionCollection::printXvgrInfo(FILE *out, output_env_t oenv) const
     }
 }
 
+// static
+HelpTopicPointer
+SelectionCollection::createDefaultHelpTopic()
+{
+    return createSelectionHelpTopic();
+}
+
 } // namespace gmx
index 5c19cf84ce12f5fe6674277109ed3dcec452ae76..80407cac45c2664809e9be4ab247484f333e9a52 100644 (file)
@@ -44,6 +44,7 @@
 
 #include "../legacyheaders/typedefs.h"
 
+#include "../onlinehelp/helptopicinterface.h"
 #include "../utility/common.h"
 #include "selection.h" // For gmx::SelectionList
 
@@ -102,6 +103,14 @@ class SelectionEvaluator;
 class SelectionCollection
 {
     public:
+        /*! \brief
+         * Creates a help tree for selections.
+         *
+         * \throws   std::bad_alloc if out of memory.
+         * \returns  Root topic of the created selection tree.
+         */
+        static HelpTopicPointer createDefaultHelpTopic();
+
         /*! \brief
          * Creates an empty selection collection.
          *
index 143ed1e70d5c4d0af85fd6b8b64ff77a20610be4..dd24332871dd80d3c3f828325d6db0e4aa4e92ce 100644 (file)
@@ -282,4 +282,32 @@ TrajectoryAnalysisCommandLineRunner::run(int argc, char *argv[])
     return 0;
 }
 
+
+void
+TrajectoryAnalysisCommandLineRunner::writeHelp(File *file)
+{
+    // TODO: This method duplicates some code from run() and Impl::printHelp().
+    // See how to best refactor it to share the common code.
+    SelectionCollection             selections;
+    SelectionOptionManager          seloptManager(&selections);
+    TrajectoryAnalysisSettings      settings;
+    TrajectoryAnalysisRunnerCommon  common(&settings);
+    Options                         options(NULL, NULL);
+
+    Options &moduleOptions    = _impl->_module->initOptions(&settings);
+    Options &commonOptions    = common.initOptions();
+    Options &selectionOptions = selections.initOptions();
+
+    options.addSubSection(&commonOptions);
+    options.addSubSection(&selectionOptions);
+    options.addSubSection(&moduleOptions);
+
+    setManagerForSelectionOptions(&options, &seloptManager);
+
+    CommandLineHelpWriter(options)
+        .setShowDescriptions(true)
+        .setTimeUnitString(settings.timeUnitManager().timeUnitAsString())
+        .writeHelp(file);
+}
+
 } // namespace gmx
index 97622541e576d8dd3a102d43e0a93926b8390023..15c21977eae76b07109552b129ab451fc1cad6bd 100644 (file)
@@ -44,6 +44,7 @@
 namespace gmx
 {
 
+class File;
 class TrajectoryAnalysisModule;
 
 /*! \brief
@@ -102,6 +103,13 @@ class TrajectoryAnalysisCommandLineRunner
          * \returns Zero on success.
          */
         int run(int argc, char *argv[]);
+        /*! \brief
+         * Prints help for the module, including common options from the runner.
+         *
+         * \param[in] file  File to write the help to.
+         * \throws    std::bad_alloc if out of memory.
+         */
+        void writeHelp(File *file);
 
     private:
         class Impl;
index 7b9087c9060794c12e4e02b4331bb866ebd9537a..c4e8cb0c82836964dea8f233d7f0cc0bf54b783a 100644 (file)
@@ -66,10 +66,11 @@ class AbstractTrajAnalysisCmdLineWrapper : public CommandLineModuleInterface
         virtual const char *shortDescription() const = 0;
 
         virtual int run(int argc, char *argv[]);
+        virtual void writeHelp(File *file) const;
 
     protected:
         //! Creates the analysis module for this wrapper.
-        virtual TrajectoryAnalysisModulePointer createModule() = 0;
+        virtual TrajectoryAnalysisModulePointer createModule() const = 0;
 };
 
 int AbstractTrajAnalysisCmdLineWrapper::run(int argc, char *argv[])
@@ -80,6 +81,13 @@ int AbstractTrajAnalysisCmdLineWrapper::run(int argc, char *argv[])
     return runner.run(argc, argv);
 }
 
+void AbstractTrajAnalysisCmdLineWrapper::writeHelp(File *file) const
+{
+    TrajectoryAnalysisModulePointer module(createModule());
+    TrajectoryAnalysisCommandLineRunner runner(module.get());
+    runner.writeHelp(file);
+}
+
 /*! \internal \brief
  * Template for command-line wrapper of a trajectory analysis module.
  *
@@ -103,7 +111,7 @@ class TrajAnalysisCmdLineWrapper : public AbstractTrajAnalysisCmdLineWrapper
         {
             return Module::shortDescription;
         }
-        virtual TrajectoryAnalysisModulePointer createModule()
+        virtual TrajectoryAnalysisModulePointer createModule() const
         {
             return TrajectoryAnalysisModulePointer(new Module);
         }
index 3fe2fbb461e6058a5013bda0d2798af674da2435..e3afd62cd77cd59a3d0c5f3a5d3f303433540004 100644 (file)
@@ -207,6 +207,13 @@ void File::writeLine()
     writeString("\n");
 }
 
+// static
+File &File::standardOutput()
+{
+    static File stdoutObject(stdout, false);
+    return stdoutObject;
+}
+
 // static
 File &File::standardError()
 {
index 8f01a7445a83ef9344fdcae7e81080584755baa9..244a74f423969b80274c0df81553a6fab429aa9d 100644 (file)
@@ -153,10 +153,16 @@ class File
          */
         void writeLine();
 
+        /*! \brief
+         * Returns a File object for accessing stdout.
+         *
+         * \throws    std::bad_alloc if out of memory (only on first call).
+         */
+        static File &standardOutput();
         /*! \brief
          * Returns a File object for accessing stderr.
          *
-         * \throws    std::bad_alloc if out of memory.
+         * \throws    std::bad_alloc if out of memory (only on first call).
          */
         static File &standardError();
 
index 5767947ace46e411a56f3eda2d967d3747b24411..574227b24bd94a43b140377efda1f017d0df3d42 100644 (file)
@@ -36,6 +36,7 @@
 #include "gromacs/legacyheaders/copyrite.h"
 
 #include "gromacs/commandline/cmdlinemodulemanager.h"
+#include "gromacs/selection/selectioncollection.h"
 #include "gromacs/trajectoryanalysis/modules.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/programinfo.h"
@@ -52,6 +53,7 @@ main(int argc, char *argv[])
     {
         gmx::CommandLineModuleManager manager(info);
         registerTrajectoryAnalysisModules(&manager);
+        manager.addHelpTopic(gmx::SelectionCollection::createDefaultHelpTopic());
         return manager.run(argc, argv);
     }
     catch (const std::exception &ex)