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