Add subsection depth to HelpWriterContext
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpmodule.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::CommandLineHelpModule.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "gmxpre.h"
43
44 #include "cmdlinehelpmodule.h"
45
46 #include <string>
47 #include <vector>
48
49 #include <boost/scoped_ptr.hpp>
50
51 #include "gromacs/commandline/cmdlinehelpcontext.h"
52 #include "gromacs/commandline/cmdlinehelpwriter.h"
53 #include "gromacs/commandline/cmdlineparser.h"
54 #include "gromacs/onlinehelp/helpformat.h"
55 #include "gromacs/onlinehelp/helpmanager.h"
56 #include "gromacs/onlinehelp/helptopic.h"
57 #include "gromacs/onlinehelp/helpwritercontext.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/utility/baseversion.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/file.h"
63 #include "gromacs/utility/fileredirector.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/programcontext.h"
66 #include "gromacs/utility/stringutil.h"
67
68 #include "shellcompletions.h"
69
70 namespace gmx
71 {
72
73 namespace
74 {
75 class HelpExportInterface;
76 class RootHelpTopic;
77 }   // namespace
78
79 /********************************************************************
80  * CommandLineHelpModuleImpl declaration
81  */
82
83 class CommandLineHelpModuleImpl
84 {
85     public:
86         CommandLineHelpModuleImpl(const ProgramContextInterface    &programContext,
87                                   const std::string                &binaryName,
88                                   const CommandLineModuleMap       &modules,
89                                   const CommandLineModuleGroupList &groups);
90
91         void exportHelp(HelpExportInterface *exporter) const;
92
93         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
94         const ProgramContextInterface    &programContext_;
95         std::string                       binaryName_;
96         const CommandLineModuleMap       &modules_;
97         const CommandLineModuleGroupList &groups_;
98
99         CommandLineHelpContext           *context_;
100         const CommandLineModuleInterface *moduleOverride_;
101         bool                              bHidden_;
102
103         FileOutputRedirectorInterface    *outputRedirector_;
104
105         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
106 };
107
108 namespace
109 {
110
111 /********************************************************************
112  * RootHelpTopic
113  */
114
115 struct RootHelpText
116 {
117     static const char        name[];
118     static const char        title[];
119     static const char *const text[];
120 };
121
122 // The first two are not used.
123 const char        RootHelpText::name[]  = "";
124 const char        RootHelpText::title[] = "";
125 const char *const RootHelpText::text[]  = { "" };
126
127 /*! \brief
128  * Help topic that forms the root of the help tree for the help subcommand.
129  *
130  * \ingroup module_commandline
131  */
132 class RootHelpTopic : public CompositeHelpTopic<RootHelpText>
133 {
134     public:
135         /*! \brief
136          * Creates a root help topic.
137          *
138          * Does not throw.
139          */
140         explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
141             : helpModule_(helpModule)
142         {
143         }
144
145         virtual void writeHelp(const HelpWriterContext &context) const;
146
147     private:
148         const CommandLineHelpModuleImpl  &helpModule_;
149
150         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
151 };
152
153 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
154 {
155     if (context.outputFormat() != eHelpOutputFormat_Console)
156     {
157         // TODO: Implement once the situation with Redmine issue #969 is more
158         // clear.
159         GMX_THROW(NotImplementedError(
160                           "Root help is not implemented for this output format"));
161     }
162     {
163         CommandLineCommonOptionsHolder optionsHolder;
164         CommandLineHelpContext         cmdlineContext(*helpModule_.context_);
165         cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
166         optionsHolder.initOptions();
167         // TODO: Add <command> [<args>] into the synopsis.
168         CommandLineHelpWriter(*optionsHolder.options())
169             .writeHelp(cmdlineContext);
170     }
171     // TODO: Consider printing a list of "core" commands. Would require someone
172     // to determine such a set...
173     writeSubTopicList(context,
174                       "Additional help is available on the following topics:");
175     // TODO: Make these respect the binary name passed in, to make tests work better.
176     context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
177     context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
178 }
179
180 /********************************************************************
181  * CommandsHelpTopic
182  */
183
184 /*! \brief
185  * Help topic for listing the commands.
186  *
187  * \ingroup module_commandline
188  */
189 class CommandsHelpTopic : public HelpTopicInterface
190 {
191     public:
192         /*! \brief
193          * Creates a command list help topic.
194          *
195          * \param[in]     helpModule Help module to get module information from.
196          *
197          * Does not throw.
198          */
199         explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
200             : helpModule_(helpModule)
201         {
202         }
203
204         virtual const char *name() const { return "commands"; }
205         virtual const char *title() const { return "List of available commands"; }
206         virtual bool hasSubTopics() const { return false; }
207         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
208         {
209             return NULL;
210         }
211
212         virtual void writeHelp(const HelpWriterContext &context) const;
213
214     private:
215         const CommandLineHelpModuleImpl &helpModule_;
216
217         GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
218 };
219
220 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
221 {
222     if (context.outputFormat() != eHelpOutputFormat_Console)
223     {
224         GMX_THROW(NotImplementedError(
225                           "Module list is not implemented for this output format"));
226     }
227     int maxNameLength = 0;
228     const CommandLineModuleMap           &modules = helpModule_.modules_;
229     CommandLineModuleMap::const_iterator  module;
230     for (module = modules.begin(); module != modules.end(); ++module)
231     {
232         int nameLength = static_cast<int>(module->first.length());
233         if (module->second->shortDescription() != NULL
234             && nameLength > maxNameLength)
235         {
236             maxNameLength = nameLength;
237         }
238     }
239     context.writeTextBlock(
240             "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
241             "Available commands:");
242     File              &file = context.outputFile();
243     TextTableFormatter formatter;
244     formatter.addColumn(NULL, maxNameLength + 1, false);
245     formatter.addColumn(NULL, 72 - maxNameLength, true);
246     formatter.setFirstColumnIndent(4);
247     for (module = modules.begin(); module != modules.end(); ++module)
248     {
249         const char *name        = module->first.c_str();
250         const char *description = module->second->shortDescription();
251         if (description != NULL)
252         {
253             formatter.clear();
254             formatter.addColumnLine(0, name);
255             formatter.addColumnLine(1, description);
256             file.writeString(formatter.formatRow());
257         }
258     }
259     context.writeTextBlock(
260             "For help on a command, use '[PROGRAM] help <command>'.");
261 }
262
263 /********************************************************************
264  * ModuleHelpTopic
265  */
266
267 /*! \brief
268  * Help topic wrapper for a command-line module.
269  *
270  * This class implements HelpTopicInterface such that it wraps a
271  * CommandLineModuleInterface, allowing subcommand "help <command>"
272  * to produce the help for "<command>".
273  *
274  * \ingroup module_commandline
275  */
276 class ModuleHelpTopic : public HelpTopicInterface
277 {
278     public:
279         //! Constructs a help topic for a specific module.
280         ModuleHelpTopic(const CommandLineModuleInterface &module,
281                         const CommandLineHelpModuleImpl  &helpModule)
282             : module_(module), helpModule_(helpModule)
283         {
284         }
285
286         virtual const char *name() const { return module_.name(); }
287         virtual const char *title() const { return NULL; }
288         virtual bool hasSubTopics() const { return false; }
289         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
290         {
291             return NULL;
292         }
293         virtual void writeHelp(const HelpWriterContext &context) const;
294
295     private:
296         const CommandLineModuleInterface &module_;
297         const CommandLineHelpModuleImpl  &helpModule_;
298
299         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
300 };
301
302 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
303 {
304     CommandLineHelpContext context(*helpModule_.context_);
305     const char *const      program = helpModule_.binaryName_.c_str();
306     context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
307     module_.writeHelp(context);
308 }
309
310 /********************************************************************
311  * HelpExportInterface
312  */
313
314 /*! \brief
315  * Callbacks for exporting help information for command-line modules.
316  *
317  * \ingroup module_commandline
318  */
319 class HelpExportInterface
320 {
321     public:
322         //! Shorthand for a list of modules contained in a group.
323         typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
324
325         virtual ~HelpExportInterface() {};
326
327         /*! \brief
328          * Called once before exporting individual modules.
329          *
330          * Can, e.g., open shared output files (e.g., if the output is written
331          * into a single file, or if a separate index is required) and write
332          * headers into them.
333          */
334         virtual void startModuleExport() = 0;
335         /*! \brief
336          * Called to export the help for each module.
337          *
338          * \param[in] module      Module for which the help should be exported.
339          * \param[in] tag         Unique tag for the module (gmx-something).
340          * \param[in] displayName Display name for the module (gmx something).
341          */
342         virtual void exportModuleHelp(
343             const CommandLineModuleInterface &module,
344             const std::string                &tag,
345             const std::string                &displayName) = 0;
346         /*! \brief
347          * Called after all modules have been exported.
348          *
349          * Can close files opened in startModuleExport(), write footers to them
350          * etc.
351          */
352         virtual void finishModuleExport() = 0;
353
354         /*! \brief
355          * Called once before exporting module groups.
356          *
357          * Can, e.g., open a single output file for listing all the groups.
358          */
359         virtual void startModuleGroupExport() = 0;
360         /*! \brief
361          * Called to export the help for each module group.
362          *
363          * \param[in] title    Title for the group.
364          * \param[in] modules  List of modules in the group.
365          */
366         virtual void exportModuleGroup(const char                *title,
367                                        const ModuleGroupContents &modules) = 0;
368         /*! \brief
369          * Called after all module groups have been exported.
370          *
371          * Can close files opened in startModuleGroupExport(), write footers to them
372          * etc.
373          */
374         virtual void finishModuleGroupExport() = 0;
375 };
376
377 /*! \internal \brief
378  * Adds hyperlinks to modules within this binary.
379  *
380  * \param[in,out] links      Links are added here.
381  * \param[in]     helpModule Help module to get module information from.
382  * \throws        std::bad_alloc if out of memory.
383  *
384  * Initializes a HelpLinks object with links to modules defined in
385  * \p helpModule.
386  *
387  * \ingroup module_commandline
388  */
389 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
390 {
391     const char *const                    program = helpModule.binaryName_.c_str();
392     CommandLineModuleMap::const_iterator module;
393     for (module = helpModule.modules_.begin();
394          module != helpModule.modules_.end();
395          ++module)
396     {
397         if (module->second->shortDescription() != NULL)
398         {
399             std::string linkName("[gmx-" + module->first + "]");
400             const char *name = module->first.c_str();
401             std::string reference(
402                     formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
403             std::string displayName(
404                     formatString("[TT]%s %s[tt]", program, name));
405             links->addLink(linkName, reference, displayName);
406         }
407     }
408 }
409
410 /********************************************************************
411  * HelpExportReStructuredText
412  */
413
414 /*! \internal \brief
415  * Implements export for web pages as reStructuredText.
416  *
417  * \ingroup module_commandline
418  */
419 class HelpExportReStructuredText : public HelpExportInterface
420 {
421     public:
422         //! Initializes reST exporter.
423         explicit HelpExportReStructuredText(
424             const CommandLineHelpModuleImpl &helpModule);
425
426         virtual void startModuleExport();
427         virtual void exportModuleHelp(
428             const CommandLineModuleInterface &module,
429             const std::string                &tag,
430             const std::string                &displayName);
431         virtual void finishModuleExport();
432
433         virtual void startModuleGroupExport();
434         virtual void exportModuleGroup(const char                *title,
435                                        const ModuleGroupContents &modules);
436         virtual void finishModuleGroupExport();
437
438     private:
439         FileOutputRedirectorInterface  *outputRedirector_;
440         HelpLinks                       links_;
441         boost::scoped_ptr<File>         indexFile_;
442         boost::scoped_ptr<File>         manPagesFile_;
443 };
444
445 HelpExportReStructuredText::HelpExportReStructuredText(
446         const CommandLineHelpModuleImpl &helpModule)
447     : outputRedirector_(helpModule.outputRedirector_),
448       links_(eHelpOutputFormat_Rst)
449 {
450     File             linksFile("links.dat", "r");
451     std::string      line;
452     while (linksFile.readLine(&line))
453     {
454         links_.addLink("[REF]." + line + "[ref]",
455                        formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
456                        line);
457         links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
458     }
459     linksFile.close();
460     initProgramLinks(&links_, helpModule);
461 }
462
463 void HelpExportReStructuredText::startModuleExport()
464 {
465     indexFile_.reset(
466             new File(outputRedirector_->openFileForWriting("programs/byname.rst")));
467     indexFile_->writeLine("Tools by Name");
468     indexFile_->writeLine("=============");
469     manPagesFile_.reset(
470             new File(outputRedirector_->openFileForWriting("conf-man.py")));
471     manPagesFile_->writeLine("man_pages = [");
472 }
473
474 void HelpExportReStructuredText::exportModuleHelp(
475         const CommandLineModuleInterface &module,
476         const std::string                &tag,
477         const std::string                &displayName)
478 {
479     // TODO: Ideally, the file would only be touched if it really changes.
480     // This would make Sphinx reruns much faster.
481     File file(outputRedirector_->openFileForWriting("programs/" + tag + ".rst"));
482     file.writeLine(formatString(".. _%s:", displayName.c_str()));
483     if (0 == displayName.compare("gmx mdrun"))
484     {
485         // Make an extra link target for the convenience of
486         // MPI-specific documentation
487         file.writeLine(".. _mdrun_mpi:");
488     }
489     file.writeLine();
490
491     CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_);
492     context.enterSubSection(displayName);
493     context.setModuleDisplayName(displayName);
494     module.writeHelp(context);
495
496     file.writeLine();
497     file.writeLine(".. only:: man");
498     file.writeLine();
499     file.writeLine("   See also");
500     file.writeLine("   --------");
501     file.writeLine();
502     file.writeLine("   :manpage:`gromacs(7)`");
503     file.writeLine();
504     file.writeLine("   More information about |Gromacs| is available at <http://www.gromacs.org/>.");
505     file.close();
506
507     indexFile_->writeLine(formatString("* :doc:`%s <%s>` - %s",
508                                        displayName.c_str(), tag.c_str(),
509                                        module.shortDescription()));
510     manPagesFile_->writeLine(
511             formatString("    ('programs/%s', '%s', \"%s\", '', 1),",
512                          tag.c_str(), tag.c_str(), module.shortDescription()));
513 }
514
515 void HelpExportReStructuredText::finishModuleExport()
516 {
517     indexFile_->close();
518     indexFile_.reset();
519     manPagesFile_->writeLine("    ('man/gromacs.7', 'gromacs', 'molecular dynamics simulation suite', '', 7)");
520     manPagesFile_->writeLine("]");
521     manPagesFile_->close();
522     manPagesFile_.reset();
523 }
524
525 void HelpExportReStructuredText::startModuleGroupExport()
526 {
527     indexFile_.reset(
528             new File(outputRedirector_->openFileForWriting("programs/bytopic.rst")));
529     indexFile_->writeLine("Tools by Topic");
530     indexFile_->writeLine("==============");
531     manPagesFile_.reset(
532             new File(outputRedirector_->openFileForWriting("man/bytopic.rst")));
533 }
534
535 void HelpExportReStructuredText::exportModuleGroup(
536         const char                *title,
537         const ModuleGroupContents &modules)
538 {
539     indexFile_->writeLine(title);
540     indexFile_->writeLine(std::string(std::strlen(title), '-'));
541     manPagesFile_->writeLine(title);
542     manPagesFile_->writeLine(std::string(std::strlen(title), '+'));
543
544     ModuleGroupContents::const_iterator module;
545     for (module = modules.begin(); module != modules.end(); ++module)
546     {
547         const std::string     &tag(module->first);
548         std::string            displayName(tag);
549         // TODO: This does not work if the binary name would contain a dash,
550         // but that is not currently the case.
551         const size_t           dashPos = displayName.find('-');
552         GMX_RELEASE_ASSERT(dashPos != std::string::npos,
553                            "There should always be at least one dash in the tag");
554         displayName[dashPos] = ' ';
555         indexFile_->writeLine(formatString("| :doc:`%s <%s>` - %s",
556                                            displayName.c_str(), tag.c_str(),
557                                            module->second));
558         manPagesFile_->writeLine(formatString("| ``%s`` - %s",
559                                               displayName.c_str(),
560                                               module->second));
561     }
562     indexFile_->writeLine();
563     manPagesFile_->writeLine();
564 }
565
566 void HelpExportReStructuredText::finishModuleGroupExport()
567 {
568     indexFile_->close();
569     indexFile_.reset();
570     manPagesFile_->close();
571     manPagesFile_.reset();
572 }
573
574 /********************************************************************
575  * HelpExportCompletion
576  */
577
578 /*! \internal \brief
579  * Implements export for command-line completion.
580  *
581  * \ingroup module_commandline
582  */
583 class HelpExportCompletion : public HelpExportInterface
584 {
585     public:
586         //! Initializes completion exporter.
587         explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
588
589         virtual void startModuleExport();
590         virtual void exportModuleHelp(
591             const CommandLineModuleInterface &module,
592             const std::string                &tag,
593             const std::string                &displayName);
594         virtual void finishModuleExport();
595
596         virtual void startModuleGroupExport() {}
597         virtual void exportModuleGroup(const char                * /*title*/,
598                                        const ModuleGroupContents & /*modules*/) {}
599         virtual void finishModuleGroupExport() {}
600
601     private:
602         ShellCompletionWriter    bashWriter_;
603         std::vector<std::string> modules_;
604 };
605
606 HelpExportCompletion::HelpExportCompletion(
607         const CommandLineHelpModuleImpl &helpModule)
608     : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
609 {
610 }
611
612 void HelpExportCompletion::startModuleExport()
613 {
614     bashWriter_.startCompletions();
615 }
616
617 void HelpExportCompletion::exportModuleHelp(
618         const CommandLineModuleInterface &module,
619         const std::string                 & /*tag*/,
620         const std::string                 & /*displayName*/)
621 {
622     modules_.push_back(module.name());
623     {
624         CommandLineHelpContext context(&bashWriter_);
625         // We use the display name to pass the name of the module to the
626         // completion writer.
627         context.setModuleDisplayName(module.name());
628         module.writeHelp(context);
629     }
630 }
631
632 void HelpExportCompletion::finishModuleExport()
633 {
634     CommandLineCommonOptionsHolder optionsHolder;
635     optionsHolder.initOptions();
636     bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
637     bashWriter_.finishCompletions();
638 }
639
640 }   // namespace
641
642 /********************************************************************
643  * CommandLineHelpModuleImpl implementation
644  */
645 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
646         const ProgramContextInterface    &programContext,
647         const std::string                &binaryName,
648         const CommandLineModuleMap       &modules,
649         const CommandLineModuleGroupList &groups)
650     : rootTopic_(new RootHelpTopic(*this)), programContext_(programContext),
651       binaryName_(binaryName), modules_(modules), groups_(groups),
652       context_(NULL), moduleOverride_(NULL), bHidden_(false),
653       outputRedirector_(&defaultFileOutputRedirector())
654 {
655 }
656
657 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter) const
658 {
659     // TODO: Would be nicer to have the file names supplied by the build system
660     // and/or export a list of files from here.
661     const char *const program = binaryName_.c_str();
662
663     exporter->startModuleExport();
664     CommandLineModuleMap::const_iterator module;
665     for (module = modules_.begin(); module != modules_.end(); ++module)
666     {
667         if (module->second->shortDescription() != NULL)
668         {
669             const char *const moduleName = module->first.c_str();
670             std::string       tag(formatString("%s-%s", program, moduleName));
671             std::string       displayName(formatString("%s %s", program, moduleName));
672             exporter->exportModuleHelp(*module->second, tag, displayName);
673         }
674     }
675     exporter->finishModuleExport();
676
677     exporter->startModuleGroupExport();
678     CommandLineModuleGroupList::const_iterator group;
679     for (group = groups_.begin(); group != groups_.end(); ++group)
680     {
681         exporter->exportModuleGroup((*group)->title(), (*group)->modules());
682     }
683     exporter->finishModuleGroupExport();
684 }
685
686 /********************************************************************
687  * CommandLineHelpModule
688  */
689
690 CommandLineHelpModule::CommandLineHelpModule(
691         const ProgramContextInterface    &programContext,
692         const std::string                &binaryName,
693         const CommandLineModuleMap       &modules,
694         const CommandLineModuleGroupList &groups)
695     : impl_(new Impl(programContext, binaryName, modules, groups))
696 {
697 }
698
699 CommandLineHelpModule::~CommandLineHelpModule()
700 {
701 }
702
703 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
704         const CommandLineModuleInterface &module) const
705 {
706     return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
707 }
708
709 void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
710 {
711     impl_->rootTopic_->addSubTopic(move(topic));
712 }
713
714 void CommandLineHelpModule::setShowHidden(bool bHidden)
715 {
716     impl_->bHidden_ = bHidden;
717 }
718
719 void CommandLineHelpModule::setModuleOverride(
720         const CommandLineModuleInterface &module)
721 {
722     impl_->moduleOverride_ = &module;
723 }
724
725 void CommandLineHelpModule::setOutputRedirector(
726         FileOutputRedirectorInterface *output)
727 {
728     impl_->outputRedirector_ = output;
729 }
730
731 int CommandLineHelpModule::run(int argc, char *argv[])
732 {
733     // Add internal topics lazily here.
734     addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)));
735
736     const char *const exportFormats[] = { "rst", "completion" };
737     std::string       exportFormat;
738     Options           options(NULL, NULL);
739     options.addOption(StringOption("export").store(&exportFormat)
740                           .enumValue(exportFormats));
741     CommandLineParser(&options).parse(&argc, argv);
742     if (!exportFormat.empty())
743     {
744         boost::scoped_ptr<HelpExportInterface> exporter;
745         if (exportFormat == "rst")
746         {
747             exporter.reset(new HelpExportReStructuredText(*impl_));
748         }
749         else if (exportFormat == "completion")
750         {
751             exporter.reset(new HelpExportCompletion(*impl_));
752         }
753         else
754         {
755             GMX_THROW(NotImplementedError("This help format is not implemented"));
756         }
757         impl_->exportHelp(exporter.get());
758         return 0;
759     }
760
761     File &outputFile = impl_->outputRedirector_->standardOutput();
762     HelpLinks                                 links(eHelpOutputFormat_Console);
763     initProgramLinks(&links, *impl_);
764     boost::scoped_ptr<CommandLineHelpContext> context(
765             new CommandLineHelpContext(&outputFile,
766                                        eHelpOutputFormat_Console, &links));
767     context->setShowHidden(impl_->bHidden_);
768     if (impl_->moduleOverride_ != NULL)
769     {
770         context->setModuleDisplayName(impl_->programContext_.displayName());
771         impl_->moduleOverride_->writeHelp(*context);
772         return 0;
773     }
774     impl_->context_ = context.get();
775
776     HelpManager helpManager(*impl_->rootTopic_, context->writerContext());
777     try
778     {
779         for (int i = 1; i < argc; ++i)
780         {
781             helpManager.enterTopic(argv[i]);
782         }
783     }
784     catch (const InvalidInputError &ex)
785     {
786         fprintf(stderr, "%s\n", ex.what());
787         return 2;
788     }
789     helpManager.writeCurrentTopic();
790     return 0;
791 }
792
793 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
794 {
795     const HelpWriterContext &writerContext = context.writerContext();
796     // TODO: Implement.
797     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
798     {
799         return;
800     }
801     writerContext.writeTextBlock(
802             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
803     // TODO: More information.
804 }
805
806 } // namespace gmx