bc883a084974685adb2028d45d22d2379b3c2d72
[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         // TODO: Make these respect the binary name passed in, to make tests work better.
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     File              &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<File>         indexFile_;
516         boost::scoped_ptr<File>         manPagesFile_;
517 };
518
519 HelpExportReStructuredText::HelpExportReStructuredText(
520         const CommandLineHelpModuleImpl &helpModule)
521     : outputRedirector_(helpModule.outputRedirector_),
522       binaryName_(helpModule.binaryName_),
523       links_(eHelpOutputFormat_Rst)
524 {
525     File             linksFile("links.dat", "r");
526     std::string      line;
527     while (linksFile.readLine(&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 File(outputRedirector_->openFileForWriting("fragments/byname.rst")));
542     indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
543                                        binaryName_.c_str(), binaryName_.c_str(),
544                                        RootHelpText::title));
545     manPagesFile_.reset(
546             new File(outputRedirector_->openFileForWriting("conf-man.py")));
547     manPagesFile_->writeLine("man_pages = [");
548 }
549
550 void HelpExportReStructuredText::exportModuleHelp(
551         const CommandLineModuleInterface &module,
552         const std::string                &tag,
553         const std::string                &displayName)
554 {
555     // TODO: Ideally, the file would only be touched if it really changes.
556     // This would make Sphinx reruns much faster.
557     File file(outputRedirector_->openFileForWriting("onlinehelp/" + tag + ".rst"));
558     file.writeLine(formatString(".. _%s:", displayName.c_str()));
559     if (0 == displayName.compare(binaryName_ + " mdrun"))
560     {
561         // Make an extra link target for the convenience of
562         // MPI-specific documentation
563         file.writeLine(".. _mdrun_mpi:");
564     }
565     file.writeLine();
566
567     CommandLineHelpContext context(&file, eHelpOutputFormat_Rst, &links_);
568     context.enterSubSection(displayName);
569     context.setModuleDisplayName(displayName);
570     module.writeHelp(context);
571
572     file.writeLine();
573     file.writeLine(".. only:: man");
574     file.writeLine();
575     file.writeLine("   See also");
576     file.writeLine("   --------");
577     file.writeLine();
578     file.writeLine(formatString("   :manpage:`%s(1)`", binaryName_.c_str()));
579     file.writeLine();
580     file.writeLine("   More information about |Gromacs| is available at <http://www.gromacs.org/>.");
581     file.close();
582
583     indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
584                                        displayName.c_str(), tag.c_str(),
585                                        module.shortDescription()));
586     manPagesFile_->writeLine(
587             formatString("    ('programs/%s', '%s', \"%s\", '', 1),",
588                          tag.c_str(), tag.c_str(), module.shortDescription()));
589 }
590
591 void HelpExportReStructuredText::finishModuleExport()
592 {
593     indexFile_->close();
594     indexFile_.reset();
595     // TODO: Generalize.
596     manPagesFile_->writeLine(
597             formatString("    ('programs/%s', '%s', '%s', '', 1)",
598                          binaryName_.c_str(), binaryName_.c_str(),
599                          RootHelpText::title));
600     manPagesFile_->writeLine("]");
601     manPagesFile_->close();
602     manPagesFile_.reset();
603 }
604
605 void HelpExportReStructuredText::startModuleGroupExport()
606 {
607     indexFile_.reset(
608             new File(outputRedirector_->openFileForWriting("fragments/bytopic.rst")));
609     manPagesFile_.reset(
610             new File(outputRedirector_->openFileForWriting("fragments/bytopic-man.rst")));
611 }
612
613 void HelpExportReStructuredText::exportModuleGroup(
614         const char                *title,
615         const ModuleGroupContents &modules)
616 {
617     indexFile_->writeLine(title);
618     indexFile_->writeLine(std::string(std::strlen(title), '^'));
619     manPagesFile_->writeLine(title);
620     manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
621
622     ModuleGroupContents::const_iterator module;
623     for (module = modules.begin(); module != modules.end(); ++module)
624     {
625         const std::string     &tag(module->first);
626         std::string            displayName(tag);
627         // TODO: This does not work if the binary name would contain a dash,
628         // but that is not currently the case.
629         const size_t           dashPos = displayName.find('-');
630         GMX_RELEASE_ASSERT(dashPos != std::string::npos,
631                            "There should always be at least one dash in the tag");
632         displayName[dashPos] = ' ';
633         indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n  %s",
634                                            displayName.c_str(), tag.c_str(),
635                                            module->second));
636         manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n  %s",
637                                               tag.c_str(),
638                                               module->second));
639     }
640     indexFile_->writeLine();
641     manPagesFile_->writeLine();
642 }
643
644 void HelpExportReStructuredText::finishModuleGroupExport()
645 {
646     indexFile_->close();
647     indexFile_.reset();
648     manPagesFile_->close();
649     manPagesFile_.reset();
650 }
651
652 void HelpExportReStructuredText::exportTopic(const HelpTopicInterface &topic)
653 {
654     const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
655     File              file(outputRedirector_->openFileForWriting(path));
656     HelpWriterContext context(&file, eHelpOutputFormat_Rst, &links_);
657     HelpManager       manager(topic, context);
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     context.setShowHidden(impl_->bHidden_);
858     if (impl_->moduleOverride_ != NULL)
859     {
860         context.setModuleDisplayName(impl_->programContext_.displayName());
861         impl_->moduleOverride_->writeHelp(context);
862         return 0;
863     }
864     impl_->context_ = &context;
865
866     HelpManager helpManager(*impl_->rootTopic_, context.writerContext());
867     try
868     {
869         for (int i = 1; i < argc; ++i)
870         {
871             helpManager.enterTopic(argv[i]);
872         }
873     }
874     catch (const InvalidInputError &ex)
875     {
876         fprintf(stderr, "%s\n", ex.what());
877         return 2;
878     }
879     helpManager.writeCurrentTopic();
880     return 0;
881 }
882
883 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
884 {
885     const HelpWriterContext &writerContext = context.writerContext();
886     // TODO: Implement.
887     if (writerContext.outputFormat() != eHelpOutputFormat_Console)
888     {
889         return;
890     }
891     writerContext.writeTextBlock(
892             "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
893     // TODO: More information.
894 }
895
896 } // namespace gmx