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