Resolve one TODO for command line help tests
[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/arrayref.h"
61 #include "gromacs/utility/baseversion.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/file.h"
64 #include "gromacs/utility/fileredirector.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/programcontext.h"
67 #include "gromacs/utility/stringutil.h"
68
69 #include "shellcompletions.h"
70
71 namespace gmx
72 {
73
74 namespace
75 {
76 class HelpExportInterface;
77 class RootHelpTopic;
78 }   // namespace
79
80 /********************************************************************
81  * CommandLineHelpModuleImpl declaration
82  */
83
84 class CommandLineHelpModuleImpl
85 {
86     public:
87         CommandLineHelpModuleImpl(const ProgramContextInterface    &programContext,
88                                   const std::string                &binaryName,
89                                   const CommandLineModuleMap       &modules,
90                                   const CommandLineModuleGroupList &groups);
91
92         void exportHelp(HelpExportInterface *exporter);
93
94         boost::scoped_ptr<RootHelpTopic>  rootTopic_;
95         const ProgramContextInterface    &programContext_;
96         std::string                       binaryName_;
97         const CommandLineModuleMap       &modules_;
98         const CommandLineModuleGroupList &groups_;
99
100         CommandLineHelpContext           *context_;
101         const CommandLineModuleInterface *moduleOverride_;
102         bool                              bHidden_;
103
104         FileOutputRedirectorInterface    *outputRedirector_;
105
106         GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
107 };
108
109 namespace
110 {
111
112 /********************************************************************
113  * HelpExportInterface
114  */
115
116 /*! \brief
117  * Callbacks for exporting help information for command-line modules.
118  *
119  * \ingroup module_commandline
120  */
121 class HelpExportInterface
122 {
123     public:
124         //! Shorthand for a list of modules contained in a group.
125         typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
126
127         virtual ~HelpExportInterface() {};
128
129         /*! \brief
130          * Called once before exporting individual modules.
131          *
132          * Can, e.g., open shared output files (e.g., if the output is written
133          * into a single file, or if a separate index is required) and write
134          * headers into them.
135          */
136         virtual void startModuleExport() = 0;
137         /*! \brief
138          * Called to export the help for each module.
139          *
140          * \param[in] module      Module for which the help should be exported.
141          * \param[in] tag         Unique tag for the module (gmx-something).
142          * \param[in] displayName Display name for the module (gmx something).
143          */
144         virtual void exportModuleHelp(
145             const CommandLineModuleInterface &module,
146             const std::string                &tag,
147             const std::string                &displayName) = 0;
148         /*! \brief
149          * Called after all modules have been exported.
150          *
151          * Can close files opened in startModuleExport(), write footers to them
152          * etc.
153          */
154         virtual void finishModuleExport() = 0;
155
156         /*! \brief
157          * Called once before exporting module groups.
158          *
159          * Can, e.g., open a single output file for listing all the groups.
160          */
161         virtual void startModuleGroupExport() = 0;
162         /*! \brief
163          * Called to export the help for each module group.
164          *
165          * \param[in] title    Title for the group.
166          * \param[in] modules  List of modules in the group.
167          */
168         virtual void exportModuleGroup(const char                *title,
169                                        const ModuleGroupContents &modules) = 0;
170         /*! \brief
171          * Called after all module groups have been exported.
172          *
173          * Can close files opened in startModuleGroupExport(), write footers to them
174          * etc.
175          */
176         virtual void finishModuleGroupExport() = 0;
177
178         /*! \brief
179          * Called to export the help for a top-level topic.
180          *
181          * \param[in] topic   Topic to export.
182          */
183         virtual void exportTopic(const HelpTopicInterface &topic) = 0;
184 };
185
186 /********************************************************************
187  * RootHelpTopic
188  */
189
190 struct RootHelpText
191 {
192     static const char        title[];
193     static const char *const text[];
194 };
195
196 // These are used for the gmx.1 man page.
197 // TODO: Do not hardcode them here, but pass them from the outside to make this
198 // code more generic.
199 const char        RootHelpText::title[] = "molecular dynamics simulation suite";
200 const char *const RootHelpText::text[]  = {
201     "|Gromacs| is a full-featured suite of programs to perform molecular",
202     "dynamics simulations, i.e., to simulate the behavior of systems with",
203     "hundreds to millions of particles using Newtonian equations of motion.",
204     "It is primarily used for research on proteins, lipids, and polymers, but",
205     "can be applied to a wide variety of chemical and biological research",
206     "questions.",
207 };
208
209 /*! \brief
210  * Help topic that forms the root of the help tree for the help subcommand.
211  *
212  * \ingroup module_commandline
213  */
214 class RootHelpTopic : public AbstractCompositeHelpTopic
215 {
216     public:
217         /*! \brief
218          * Creates a root help topic.
219          *
220          * Does not throw.
221          */
222         explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
223             : helpModule_(helpModule)
224         {
225         }
226
227         virtual const char *name() const { return helpModule_.binaryName_.c_str(); }
228         virtual const char *title() const { return title_.c_str(); }
229
230         //! Adds a top-level topic and optionally marks it as exported.
231         void addTopic(HelpTopicPointer topic, bool bExported)
232         {
233             if (bExported)
234             {
235                 exportedTopics_.push_back(topic->name());
236             }
237             addSubTopic(move(topic));
238         }
239         //! Exports all the top-level topics with the given exporter.
240         void exportHelp(HelpExportInterface *exporter);
241
242         virtual void writeHelp(const HelpWriterContext &context) const;
243
244     private:
245         // unused because of the writeHelp() override
246         virtual std::string helpText() const { return ""; }
247
248         const CommandLineHelpModuleImpl  &helpModule_;
249         std::string                       title_;
250         std::vector<std::string>          exportedTopics_;
251
252         GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
253 };
254
255 void RootHelpTopic::exportHelp(HelpExportInterface *exporter)
256 {
257     std::vector<std::string>::const_iterator topicName;
258     for (topicName = exportedTopics_.begin();
259          topicName != exportedTopics_.end();
260          ++topicName)
261     {
262         const HelpTopicInterface *topic = findSubTopic(topicName->c_str());
263         GMX_RELEASE_ASSERT(topic != NULL, "Exported help topic no longer found");
264         exporter->exportTopic(*topic);
265     }
266     // For now, the title is only set for the export to make it not appear in
267     // console output, which makes things consistent for 'gmx help' and
268     // 'gmx help <command>'.
269     title_ = RootHelpText::title;
270     exporter->exportTopic(*this);
271 }
272
273 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
274 {
275     {
276         CommandLineCommonOptionsHolder            optionsHolder;
277         boost::scoped_ptr<CommandLineHelpContext> cmdlineContext;
278         if (helpModule_.context_ != NULL)
279         {
280             cmdlineContext.reset(new CommandLineHelpContext(*helpModule_.context_));
281         }
282         else
283         {
284             cmdlineContext.reset(new CommandLineHelpContext(context));
285         }
286         cmdlineContext->setModuleDisplayName(helpModule_.binaryName_);
287         optionsHolder.initOptions();
288         Options &options = *optionsHolder.options();
289         options.setDescription(RootHelpText::text);
290         // TODO: Add <command> [<args>] into the synopsis.
291         CommandLineHelpWriter(options)
292             .setShowDescriptions(context.outputFormat() != eHelpOutputFormat_Console)
293             .writeHelp(*cmdlineContext);
294     }
295     if (context.outputFormat() == eHelpOutputFormat_Console)
296     {
297         // TODO: Consider printing a list of "core" commands. Would require someone
298         // to determine such a set...
299         writeSubTopicList(context,
300                           "Additional help is available on the following topics:");
301         context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
302         context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
303     }
304     else
305     {
306         // TODO: This should not really end up on the HTML page.
307         context.writeTitle(formatString("%s commands", helpModule_.binaryName_.c_str()));
308         context.writeTextBlock(
309                 "The following commands are available. Please refer to their "
310                 "individual man pages or [TT][PROGRAM] help <command>[tt] "
311                 "for further details.");
312         context.writeTextBlock("");
313         context.writeTextBlock(".. include:: /fragments/bytopic-man.rst");
314     }
315 }
316
317 /********************************************************************
318  * CommandsHelpTopic
319  */
320
321 /*! \brief
322  * Help topic for listing the commands.
323  *
324  * \ingroup module_commandline
325  */
326 class CommandsHelpTopic : public HelpTopicInterface
327 {
328     public:
329         /*! \brief
330          * Creates a command list help topic.
331          *
332          * \param[in]     helpModule Help module to get module information from.
333          *
334          * Does not throw.
335          */
336         explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
337             : helpModule_(helpModule)
338         {
339         }
340
341         virtual const char *name() const { return "commands"; }
342         virtual const char *title() const { return "List of available commands"; }
343         virtual bool hasSubTopics() const { return false; }
344         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
345         {
346             return NULL;
347         }
348
349         virtual void writeHelp(const HelpWriterContext &context) const;
350
351     private:
352         const CommandLineHelpModuleImpl &helpModule_;
353
354         GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
355 };
356
357 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
358 {
359     if (context.outputFormat() != eHelpOutputFormat_Console)
360     {
361         GMX_THROW(NotImplementedError(
362                           "Module list is not implemented for this output format"));
363     }
364     int maxNameLength = 0;
365     const CommandLineModuleMap           &modules = helpModule_.modules_;
366     CommandLineModuleMap::const_iterator  module;
367     for (module = modules.begin(); module != modules.end(); ++module)
368     {
369         int nameLength = static_cast<int>(module->first.length());
370         if (module->second->shortDescription() != NULL
371             && nameLength > maxNameLength)
372         {
373             maxNameLength = nameLength;
374         }
375     }
376     context.writeTextBlock(
377             "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
378             "Available commands:");
379     File              &file = context.outputFile();
380     TextTableFormatter formatter;
381     formatter.addColumn(NULL, maxNameLength + 1, false);
382     formatter.addColumn(NULL, 72 - maxNameLength, true);
383     formatter.setFirstColumnIndent(4);
384     for (module = modules.begin(); module != modules.end(); ++module)
385     {
386         const char *name        = module->first.c_str();
387         const char *description = module->second->shortDescription();
388         if (description != NULL)
389         {
390             formatter.clear();
391             formatter.addColumnLine(0, name);
392             formatter.addColumnLine(1, description);
393             file.writeString(formatter.formatRow());
394         }
395     }
396     context.writeTextBlock(
397             "For help on a command, use '[PROGRAM] help <command>'.");
398 }
399
400 /********************************************************************
401  * ModuleHelpTopic
402  */
403
404 /*! \brief
405  * Help topic wrapper for a command-line module.
406  *
407  * This class implements HelpTopicInterface such that it wraps a
408  * CommandLineModuleInterface, allowing subcommand "help <command>"
409  * to produce the help for "<command>".
410  *
411  * \ingroup module_commandline
412  */
413 class ModuleHelpTopic : public HelpTopicInterface
414 {
415     public:
416         //! Constructs a help topic for a specific module.
417         ModuleHelpTopic(const CommandLineModuleInterface &module,
418                         const CommandLineHelpModuleImpl  &helpModule)
419             : module_(module), helpModule_(helpModule)
420         {
421         }
422
423         virtual const char *name() const { return module_.name(); }
424         virtual const char *title() const { return NULL; }
425         virtual bool hasSubTopics() const { return false; }
426         virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
427         {
428             return NULL;
429         }
430         virtual void writeHelp(const HelpWriterContext &context) const;
431
432     private:
433         const CommandLineModuleInterface &module_;
434         const CommandLineHelpModuleImpl  &helpModule_;
435
436         GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
437 };
438
439 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
440 {
441     CommandLineHelpContext context(*helpModule_.context_);
442     const char *const      program = helpModule_.binaryName_.c_str();
443     context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
444     module_.writeHelp(context);
445 }
446
447 /********************************************************************
448  * HelpExportReStructuredText
449  */
450
451 /*! \internal \brief
452  * Adds hyperlinks to modules within this binary.
453  *
454  * \param[in,out] links      Links are added here.
455  * \param[in]     helpModule Help module to get module information from.
456  * \throws        std::bad_alloc if out of memory.
457  *
458  * Initializes a HelpLinks object with links to modules defined in
459  * \p helpModule.
460  *
461  * \ingroup module_commandline
462  */
463 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
464 {
465     const char *const                    program = helpModule.binaryName_.c_str();
466     CommandLineModuleMap::const_iterator module;
467     for (module = helpModule.modules_.begin();
468          module != helpModule.modules_.end();
469          ++module)
470     {
471         if (module->second->shortDescription() != NULL)
472         {
473             std::string linkName("[gmx-" + module->first + "]");
474             const char *name = module->first.c_str();
475             std::string reference(
476                     formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
477             std::string displayName(
478                     formatString("[TT]%s %s[tt]", program, name));
479             links->addLink(linkName, reference, displayName);
480         }
481     }
482 }
483
484 /*! \internal \brief
485  * Implements export for web pages as reStructuredText.
486  *
487  * \ingroup module_commandline
488  */
489 class HelpExportReStructuredText : public HelpExportInterface
490 {
491     public:
492         //! Initializes reST exporter.
493         explicit HelpExportReStructuredText(
494             const CommandLineHelpModuleImpl &helpModule);
495
496         virtual void startModuleExport();
497         virtual void exportModuleHelp(
498             const CommandLineModuleInterface &module,
499             const std::string                &tag,
500             const std::string                &displayName);
501         virtual void finishModuleExport();
502
503         virtual void startModuleGroupExport();
504         virtual void exportModuleGroup(const char                *title,
505                                        const ModuleGroupContents &modules);
506         virtual void finishModuleGroupExport();
507
508         virtual void exportTopic(const HelpTopicInterface &topic);
509
510     private:
511         FileOutputRedirectorInterface  *outputRedirector_;
512         const std::string              &binaryName_;
513         HelpLinks                       links_;
514         boost::scoped_ptr<File>         indexFile_;
515         boost::scoped_ptr<File>         manPagesFile_;
516 };
517
518 HelpExportReStructuredText::HelpExportReStructuredText(
519         const CommandLineHelpModuleImpl &helpModule)
520     : outputRedirector_(helpModule.outputRedirector_),
521       binaryName_(helpModule.binaryName_),
522       links_(eHelpOutputFormat_Rst)
523 {
524     File             linksFile("links.dat", "r");
525     std::string      line;
526     while (linksFile.readLine(&line))
527     {
528         links_.addLink("[REF]." + line + "[ref]",
529                        formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
530                        line);
531         links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
532     }
533     linksFile.close();
534     initProgramLinks(&links_, helpModule);
535 }
536
537 void HelpExportReStructuredText::startModuleExport()
538 {
539     indexFile_.reset(
540             new File(outputRedirector_->openFileForWriting("fragments/byname.rst")));
541     indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
542                                        binaryName_.c_str(), binaryName_.c_str(),
543                                        RootHelpText::title));
544     manPagesFile_.reset(
545             new File(outputRedirector_->openFileForWriting("conf-man.py")));
546     manPagesFile_->writeLine("man_pages = [");
547 }
548
549 void HelpExportReStructuredText::exportModuleHelp(
550         const CommandLineModuleInterface &module,
551         const std::string                &tag,
552         const std::string                &displayName)
553 {
554     // TODO: Ideally, the file would only be touched if it really changes.
555     // This would make Sphinx reruns much faster.
556     File file(outputRedirector_->openFileForWriting("onlinehelp/" + tag + ".rst"));
557     file.writeLine(formatString(".. _%s:", displayName.c_str()));
558     if (0 == displayName.compare(binaryName_ + " mdrun"))
559     {
560         // Make an extra link target for the convenience of
561         // MPI-specific documentation
562         file.writeLine(".. _mdrun_mpi:");
563     }
564     file.writeLine();
565
566     CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_, binaryName_);
567     context.enterSubSection(displayName);
568     context.setModuleDisplayName(displayName);
569     module.writeHelp(context);
570
571     file.writeLine();
572     file.writeLine(".. only:: man");
573     file.writeLine();
574     file.writeLine("   See also");
575     file.writeLine("   --------");
576     file.writeLine();
577     file.writeLine(formatString("   :manpage:`%s(1)`", binaryName_.c_str()));
578     file.writeLine();
579     file.writeLine("   More information about |Gromacs| is available at <http://www.gromacs.org/>.");
580     file.close();
581
582     indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
583                                        displayName.c_str(), tag.c_str(),
584                                        module.shortDescription()));
585     manPagesFile_->writeLine(
586             formatString("    ('programs/%s', '%s', \"%s\", '', 1),",
587                          tag.c_str(), tag.c_str(), module.shortDescription()));
588 }
589
590 void HelpExportReStructuredText::finishModuleExport()
591 {
592     indexFile_->close();
593     indexFile_.reset();
594     // TODO: Generalize.
595     manPagesFile_->writeLine(
596             formatString("    ('programs/%s', '%s', '%s', '', 1)",
597                          binaryName_.c_str(), binaryName_.c_str(),
598                          RootHelpText::title));
599     manPagesFile_->writeLine("]");
600     manPagesFile_->close();
601     manPagesFile_.reset();
602 }
603
604 void HelpExportReStructuredText::startModuleGroupExport()
605 {
606     indexFile_.reset(
607             new File(outputRedirector_->openFileForWriting("fragments/bytopic.rst")));
608     manPagesFile_.reset(
609             new File(outputRedirector_->openFileForWriting("fragments/bytopic-man.rst")));
610 }
611
612 void HelpExportReStructuredText::exportModuleGroup(
613         const char                *title,
614         const ModuleGroupContents &modules)
615 {
616     indexFile_->writeLine(title);
617     indexFile_->writeLine(std::string(std::strlen(title), '^'));
618     manPagesFile_->writeLine(title);
619     manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
620
621     ModuleGroupContents::const_iterator module;
622     for (module = modules.begin(); module != modules.end(); ++module)
623     {
624         const std::string     &tag(module->first);
625         std::string            displayName(tag);
626         // TODO: This does not work if the binary name would contain a dash,
627         // but that is not currently the case.
628         const size_t           dashPos = displayName.find('-');
629         GMX_RELEASE_ASSERT(dashPos != std::string::npos,
630                            "There should always be at least one dash in the tag");
631         displayName[dashPos] = ' ';
632         indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n  %s",
633                                            displayName.c_str(), tag.c_str(),
634                                            module->second));
635         manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n  %s",
636                                               tag.c_str(),
637                                               module->second));
638     }
639     indexFile_->writeLine();
640     manPagesFile_->writeLine();
641 }
642
643 void HelpExportReStructuredText::finishModuleGroupExport()
644 {
645     indexFile_->close();
646     indexFile_.reset();
647     manPagesFile_->close();
648     manPagesFile_.reset();
649 }
650
651 void HelpExportReStructuredText::exportTopic(const HelpTopicInterface &topic)
652 {
653     const std::string      path("onlinehelp/" + std::string(topic.name()) + ".rst");
654     File                   file(outputRedirector_->openFileForWriting(path));
655     CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_,
656                                    binaryName_);
657     HelpManager            manager(topic, context.writerContext());
658     manager.writeCurrentTopic();
659 }
660
661 /********************************************************************
662  * HelpExportCompletion
663  */
664
665 /*! \internal \brief
666  * Implements export for command-line completion.
667  *
668  * \ingroup module_commandline
669  */
670 class HelpExportCompletion : public HelpExportInterface
671 {
672     public:
673         //! Initializes completion exporter.
674         explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
675
676         virtual void startModuleExport();
677         virtual void exportModuleHelp(
678             const CommandLineModuleInterface &module,
679             const std::string                &tag,
680             const std::string                &displayName);
681         virtual void finishModuleExport();
682
683         virtual void startModuleGroupExport() {}
684         virtual void exportModuleGroup(const char                * /*title*/,
685                                        const ModuleGroupContents & /*modules*/) {}
686         virtual void finishModuleGroupExport() {}
687
688         virtual void exportTopic(const HelpTopicInterface & /*topic*/) {}
689
690     private:
691         ShellCompletionWriter    bashWriter_;
692         std::vector<std::string> modules_;
693 };
694
695 HelpExportCompletion::HelpExportCompletion(
696         const CommandLineHelpModuleImpl &helpModule)
697     : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
698 {
699 }
700
701 void HelpExportCompletion::startModuleExport()
702 {
703     bashWriter_.startCompletions();
704 }
705
706 void HelpExportCompletion::exportModuleHelp(
707         const CommandLineModuleInterface &module,
708         const std::string                 & /*tag*/,
709         const std::string                 & /*displayName*/)
710 {
711     modules_.push_back(module.name());
712     {
713         CommandLineHelpContext context(&bashWriter_);
714         // We use the display name to pass the name of the module to the
715         // completion writer.
716         context.setModuleDisplayName(module.name());
717         module.writeHelp(context);
718     }
719 }
720
721 void HelpExportCompletion::finishModuleExport()
722 {
723     CommandLineCommonOptionsHolder optionsHolder;
724     optionsHolder.initOptions();
725     bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
726     bashWriter_.finishCompletions();
727 }
728
729 }   // namespace
730
731 /********************************************************************
732  * CommandLineHelpModuleImpl implementation
733  */
734
735 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
736         const ProgramContextInterface    &programContext,
737         const std::string                &binaryName,
738         const CommandLineModuleMap       &modules,
739         const CommandLineModuleGroupList &groups)
740     : rootTopic_(new RootHelpTopic(*this)), programContext_(programContext),
741       binaryName_(binaryName), modules_(modules), groups_(groups),
742       context_(NULL), moduleOverride_(NULL), bHidden_(false),
743       outputRedirector_(&defaultFileOutputRedirector())
744 {
745 }
746
747 void CommandLineHelpModuleImpl::exportHelp(HelpExportInterface *exporter)
748 {
749     // TODO: Would be nicer to have the file names supplied by the build system
750     // and/or export a list of files from here.
751     const char *const program = binaryName_.c_str();
752
753     exporter->startModuleExport();
754     CommandLineModuleMap::const_iterator module;
755     for (module = modules_.begin(); module != modules_.end(); ++module)
756     {
757         if (module->second->shortDescription() != NULL)
758         {
759             const char *const moduleName = module->first.c_str();
760             std::string       tag(formatString("%s-%s", program, moduleName));
761             std::string       displayName(formatString("%s %s", program, moduleName));
762             exporter->exportModuleHelp(*module->second, tag, displayName);
763         }
764     }
765     exporter->finishModuleExport();
766
767     exporter->startModuleGroupExport();
768     CommandLineModuleGroupList::const_iterator group;
769     for (group = groups_.begin(); group != groups_.end(); ++group)
770     {
771         exporter->exportModuleGroup((*group)->title(), (*group)->modules());
772     }
773     exporter->finishModuleGroupExport();
774
775     rootTopic_->exportHelp(exporter);
776 }
777
778 /********************************************************************
779  * CommandLineHelpModule
780  */
781
782 CommandLineHelpModule::CommandLineHelpModule(
783         const ProgramContextInterface    &programContext,
784         const std::string                &binaryName,
785         const CommandLineModuleMap       &modules,
786         const CommandLineModuleGroupList &groups)
787     : impl_(new Impl(programContext, binaryName, modules, groups))
788 {
789 }
790
791 CommandLineHelpModule::~CommandLineHelpModule()
792 {
793 }
794
795 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
796         const CommandLineModuleInterface &module) const
797 {
798     return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
799 }
800
801 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
802 {
803     impl_->rootTopic_->addTopic(move(topic), bExported);
804 }
805
806 void CommandLineHelpModule::setShowHidden(bool bHidden)
807 {
808     impl_->bHidden_ = bHidden;
809 }
810
811 void CommandLineHelpModule::setModuleOverride(
812         const CommandLineModuleInterface &module)
813 {
814     impl_->moduleOverride_ = &module;
815 }
816
817 void CommandLineHelpModule::setOutputRedirector(
818         FileOutputRedirectorInterface *output)
819 {
820     impl_->outputRedirector_ = output;
821 }
822
823 int CommandLineHelpModule::run(int argc, char *argv[])
824 {
825     // Add internal topics lazily here.
826     addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
827
828     const char *const exportFormats[] = { "rst", "completion" };
829     std::string       exportFormat;
830     Options           options(NULL, NULL);
831     options.addOption(StringOption("export").store(&exportFormat)
832                           .enumValue(exportFormats));
833     CommandLineParser(&options).parse(&argc, argv);
834     if (!exportFormat.empty())
835     {
836         boost::scoped_ptr<HelpExportInterface> exporter;
837         if (exportFormat == "rst")
838         {
839             exporter.reset(new HelpExportReStructuredText(*impl_));
840         }
841         else if (exportFormat == "completion")
842         {
843             exporter.reset(new HelpExportCompletion(*impl_));
844         }
845         else
846         {
847             GMX_THROW(NotImplementedError("This help format is not implemented"));
848         }
849         impl_->exportHelp(exporter.get());
850         return 0;
851     }
852
853     File                  &outputFile = impl_->outputRedirector_->standardOutput();
854     HelpLinks              links(eHelpOutputFormat_Console);
855     initProgramLinks(&links, *impl_);
856     CommandLineHelpContext context(&outputFile, eHelpOutputFormat_Console, &links,
857                                    impl_->binaryName_);
858     context.setShowHidden(impl_->bHidden_);
859     if (impl_->moduleOverride_ != NULL)
860     {
861         context.setModuleDisplayName(impl_->programContext_.displayName());
862         impl_->moduleOverride_->writeHelp(context);
863         return 0;
864     }
865     impl_->context_ = &context;
866
867     HelpManager helpManager(*impl_->rootTopic_, context.writerContext());
868     try
869     {
870         for (int i = 1; i < argc; ++i)
871         {
872             helpManager.enterTopic(argv[i]);
873         }
874     }
875     catch (const InvalidInputError &ex)
876     {
877         fprintf(stderr, "%s\n", ex.what());
878         return 2;
879     }
880     helpManager.writeCurrentTopic();
881     return 0;
882 }
883
884 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
885 {
886     const HelpWriterContext &writerContext = context.writerContext();
887     // TODO: Implement.
888     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
889     {
890         return;
891     }
892     writerContext.writeTextBlock(
893             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
894     // TODO: More information.
895 }
896
897 } // namespace gmx