Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / commandline / cmdlinehelpmodule.cpp
blob755617245eaa0d34e8ba83024dfbb4a409b3b428
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, 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.
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.
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.
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.
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.
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.
35 /*! \internal \file
36 * \brief
37 * Implements gmx::CommandLineHelpModule.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_commandline
42 #include "gmxpre.h"
44 #include "cmdlinehelpmodule.h"
46 #include <memory>
47 #include <string>
48 #include <vector>
50 #include "gromacs/commandline/cmdlinehelpcontext.h"
51 #include "gromacs/commandline/cmdlinehelpwriter.h"
52 #include "gromacs/commandline/cmdlineparser.h"
53 #include "gromacs/onlinehelp/helpformat.h"
54 #include "gromacs/onlinehelp/helpmanager.h"
55 #include "gromacs/onlinehelp/helptopic.h"
56 #include "gromacs/onlinehelp/helpwritercontext.h"
57 #include "gromacs/options/basicoptions.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/utility/arrayref.h"
60 #include "gromacs/utility/baseversion.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/fileredirector.h"
63 #include "gromacs/utility/gmxassert.h"
64 #include "gromacs/utility/path.h"
65 #include "gromacs/utility/programcontext.h"
66 #include "gromacs/utility/stringstream.h"
67 #include "gromacs/utility/stringutil.h"
68 #include "gromacs/utility/textreader.h"
69 #include "gromacs/utility/textstream.h"
70 #include "gromacs/utility/textwriter.h"
72 #include "shellcompletions.h"
74 namespace gmx
77 namespace
79 class IHelpExport;
81 /********************************************************************
82 * RootHelpTopic declaration
85 /*! \brief
86 * Help topic that forms the root of the help tree for the help subcommand.
88 * \ingroup module_commandline
90 class RootHelpTopic : public AbstractCompositeHelpTopic
92 public:
93 /*! \brief
94 * Creates a root help topic.
96 * Does not throw.
98 explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
99 : helpModule_(helpModule)
103 const char *name() const override;
104 const char *title() const override { return title_.c_str(); }
106 //! Adds a top-level topic and optionally marks it as exported.
107 void addTopic(HelpTopicPointer topic, bool bExported)
109 if (bExported)
111 exportedTopics_.emplace_back(topic->name());
113 addSubTopic(std::move(topic));
115 //! Exports all the top-level topics with the given exporter.
116 void exportHelp(IHelpExport *exporter);
118 void writeHelp(const HelpWriterContext &context) const override;
120 private:
121 // unused because of the writeHelp() override
122 std::string helpText() const override { return ""; }
124 CommandLineHelpContext createContext(const HelpWriterContext &context) const;
126 const CommandLineHelpModuleImpl &helpModule_;
127 std::string title_;
128 std::vector<std::string> exportedTopics_;
130 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
133 } // namespace
135 /********************************************************************
136 * CommandLineHelpModuleImpl declaration
139 class CommandLineHelpModuleImpl
141 public:
142 CommandLineHelpModuleImpl(const IProgramContext &programContext,
143 const std::string &binaryName,
144 const CommandLineModuleMap &modules,
145 const CommandLineModuleGroupList &groups);
147 std::unique_ptr<IHelpExport> createExporter(
148 const std::string &format,
149 IFileOutputRedirector *redirector);
150 void exportHelp(IHelpExport *exporter);
152 RootHelpTopic rootTopic_;
153 const IProgramContext &programContext_;
154 std::string binaryName_;
155 const CommandLineModuleMap &modules_;
156 const CommandLineModuleGroupList &groups_;
158 CommandLineHelpContext *context_;
159 const ICommandLineModule *moduleOverride_;
160 bool bHidden_;
162 IFileOutputRedirector *outputRedirector_;
164 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
167 namespace
170 /********************************************************************
171 * IHelpExport
174 /*! \brief
175 * Callbacks for exporting help information for command-line modules.
177 * \ingroup module_commandline
179 class IHelpExport
181 public:
182 //! Shorthand for a list of modules contained in a group.
183 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
185 virtual ~IHelpExport() {}
187 /*! \brief
188 * Called once before exporting individual modules.
190 * Can, e.g., open shared output files (e.g., if the output is written
191 * into a single file, or if a separate index is required) and write
192 * headers into them.
194 virtual void startModuleExport() = 0;
195 /*! \brief
196 * Called to export the help for each module.
198 * \param[in] module Module for which the help should be exported.
199 * \param[in] tag Unique tag for the module (gmx-something).
200 * \param[in] displayName Display name for the module (gmx something).
202 virtual void exportModuleHelp(
203 const ICommandLineModule &module,
204 const std::string &tag,
205 const std::string &displayName) = 0;
206 /*! \brief
207 * Called after all modules have been exported.
209 * Can close files opened in startModuleExport(), write footers to them
210 * etc.
212 virtual void finishModuleExport() = 0;
214 /*! \brief
215 * Called once before exporting module groups.
217 * Can, e.g., open a single output file for listing all the groups.
219 virtual void startModuleGroupExport() = 0;
220 /*! \brief
221 * Called to export the help for each module group.
223 * \param[in] title Title for the group.
224 * \param[in] modules List of modules in the group.
226 virtual void exportModuleGroup(const char *title,
227 const ModuleGroupContents &modules) = 0;
228 /*! \brief
229 * Called after all module groups have been exported.
231 * Can close files opened in startModuleGroupExport(), write footers to them
232 * etc.
234 virtual void finishModuleGroupExport() = 0;
236 /*! \brief
237 * Called to export the help for a top-level topic.
239 * \param[in] topic Topic to export.
241 virtual void exportTopic(const IHelpTopic &topic) = 0;
244 /********************************************************************
245 * RootHelpTopic implementation
248 struct RootHelpText
250 static const char title[];
251 static const char *const text[];
254 // These are used for the gmx.1 man page.
255 // TODO: Do not hardcode them here, but pass them from the outside to make this
256 // code more generic.
257 const char RootHelpText::title[] = "molecular dynamics simulation suite";
258 const char *const RootHelpText::text[] = {
259 "|Gromacs| is a full-featured suite of programs to perform molecular",
260 "dynamics simulations, i.e., to simulate the behavior of systems with",
261 "hundreds to millions of particles using Newtonian equations of motion.",
262 "It is primarily used for research on proteins, lipids, and polymers, but",
263 "can be applied to a wide variety of chemical and biological research",
264 "questions.",
267 const char *RootHelpTopic::name() const
269 return helpModule_.binaryName_.c_str();
272 void RootHelpTopic::exportHelp(IHelpExport *exporter)
274 std::vector<std::string>::const_iterator topicName;
275 for (topicName = exportedTopics_.begin();
276 topicName != exportedTopics_.end();
277 ++topicName)
279 const IHelpTopic *topic = findSubTopic(topicName->c_str());
280 GMX_RELEASE_ASSERT(topic != nullptr, "Exported help topic no longer found");
281 exporter->exportTopic(*topic);
283 // For now, the title is only set for the export to make it not appear in
284 // console output, which makes things consistent for 'gmx help' and
285 // 'gmx help <command>'.
286 title_ = RootHelpText::title;
287 exporter->exportTopic(*this);
290 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
293 CommandLineCommonOptionsHolder optionsHolder;
294 CommandLineHelpContext cmdlineContext(createContext(context));
295 cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
296 optionsHolder.initOptions();
297 Options &options = *optionsHolder.options();
298 ArrayRef<const char *const> helpText;
299 if (context.outputFormat() != eHelpOutputFormat_Console)
301 helpText = RootHelpText::text;
303 // TODO: Add <command> [<args>] into the synopsis.
304 CommandLineHelpWriter(options)
305 .setHelpText(helpText)
306 .writeHelp(cmdlineContext);
308 if (context.outputFormat() == eHelpOutputFormat_Console)
310 // TODO: Consider printing a list of "core" commands. Would require someone
311 // to determine such a set...
312 context.paragraphBreak();
313 writeSubTopicList(context,
314 "Additional help is available on the following topics:");
315 context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
316 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
318 else
320 // TODO: This should not really end up on the HTML page.
321 context.writeTitle(formatString("%s commands", helpModule_.binaryName_.c_str()));
322 context.writeTextBlock(
323 "The following commands are available. Please refer to their "
324 "individual man pages or [TT][PROGRAM] help <command>[tt] "
325 "for further details.");
326 context.writeTextBlock("");
327 context.writeTextBlock(".. include:: /fragments/bytopic-man.rst");
331 CommandLineHelpContext
332 RootHelpTopic::createContext(const HelpWriterContext &context) const
334 if (helpModule_.context_ != nullptr)
336 return CommandLineHelpContext(*helpModule_.context_);
338 else
340 return CommandLineHelpContext(context);
344 /********************************************************************
345 * CommandsHelpTopic
348 /*! \brief
349 * Help topic for listing the commands.
351 * \ingroup module_commandline
353 class CommandsHelpTopic : public IHelpTopic
355 public:
356 /*! \brief
357 * Creates a command list help topic.
359 * \param[in] helpModule Help module to get module information from.
361 * Does not throw.
363 explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
364 : helpModule_(helpModule)
368 const char *name() const override { return "commands"; }
369 const char *title() const override { return "List of available commands"; }
370 bool hasSubTopics() const override { return false; }
371 const IHelpTopic *findSubTopic(const char * /*name*/) const override
373 return nullptr;
376 void writeHelp(const HelpWriterContext &context) const override;
378 private:
379 const CommandLineHelpModuleImpl &helpModule_;
381 GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
384 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
386 if (context.outputFormat() != eHelpOutputFormat_Console)
388 GMX_THROW(NotImplementedError(
389 "Module list is not implemented for this output format"));
391 int maxNameLength = 0;
392 const CommandLineModuleMap &modules = helpModule_.modules_;
393 CommandLineModuleMap::const_iterator module;
394 for (module = modules.begin(); module != modules.end(); ++module)
396 int nameLength = static_cast<int>(module->first.length());
397 if (module->second->shortDescription() != nullptr
398 && nameLength > maxNameLength)
400 maxNameLength = nameLength;
403 context.writeTextBlock(
404 "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
405 "Available commands:");
406 TextWriter &file = context.outputFile();
407 TextTableFormatter formatter;
408 formatter.addColumn(nullptr, maxNameLength + 1, false);
409 formatter.addColumn(nullptr, 72 - maxNameLength, true);
410 formatter.setFirstColumnIndent(4);
411 for (module = modules.begin(); module != modules.end(); ++module)
413 const char *name = module->first.c_str();
414 const char *description = module->second->shortDescription();
415 if (description != nullptr)
417 formatter.clear();
418 formatter.addColumnLine(0, name);
419 formatter.addColumnLine(1, description);
420 file.writeString(formatter.formatRow());
423 context.writeTextBlock(
424 "For help on a command, use '[PROGRAM] help <command>'.");
427 /********************************************************************
428 * ModuleHelpTopic
431 /*! \brief
432 * Help topic wrapper for a command-line module.
434 * This class implements IHelpTopic such that it wraps a
435 * ICommandLineModule, allowing subcommand "help <command>"
436 * to produce the help for "<command>".
438 * \ingroup module_commandline
440 class ModuleHelpTopic : public IHelpTopic
442 public:
443 //! Constructs a help topic for a specific module.
444 ModuleHelpTopic(const ICommandLineModule &module,
445 const CommandLineHelpModuleImpl &helpModule)
446 : module_(module), helpModule_(helpModule)
450 const char *name() const override { return module_.name(); }
451 const char *title() const override { return nullptr; }
452 bool hasSubTopics() const override { return false; }
453 const IHelpTopic *findSubTopic(const char * /*name*/) const override
455 return nullptr;
457 void writeHelp(const HelpWriterContext &context) const override;
459 private:
460 const ICommandLineModule &module_;
461 const CommandLineHelpModuleImpl &helpModule_;
463 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
466 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
468 CommandLineHelpContext context(*helpModule_.context_);
469 const char *const program = helpModule_.binaryName_.c_str();
470 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
471 module_.writeHelp(context);
474 /********************************************************************
475 * HelpExportReStructuredText
478 /*! \internal \brief
479 * Adds hyperlinks to modules within this binary.
481 * \param[in,out] links Links are added here.
482 * \param[in] helpModule Help module to get module information from.
483 * \throws std::bad_alloc if out of memory.
485 * Initializes a HelpLinks object with links to modules defined in
486 * \p helpModule.
488 * \ingroup module_commandline
490 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
492 const char *const program = helpModule.binaryName_.c_str();
493 CommandLineModuleMap::const_iterator module;
494 for (module = helpModule.modules_.begin();
495 module != helpModule.modules_.end();
496 ++module)
498 if (module->second->shortDescription() != nullptr)
500 std::string linkName("[gmx-" + module->first + "]");
501 const char *name = module->first.c_str();
502 std::string reference(
503 formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
504 std::string displayName(
505 formatString("[TT]%s %s[tt]", program, name));
506 links->addLink(linkName, reference, displayName);
511 /*! \internal \brief
512 * Implements export for web pages as reStructuredText.
514 * \ingroup module_commandline
516 class HelpExportReStructuredText : public IHelpExport
518 public:
519 //! Initializes reST exporter.
520 HelpExportReStructuredText(
521 const CommandLineHelpModuleImpl &helpModule,
522 IFileOutputRedirector *outputRedirector);
524 void startModuleExport() override;
525 void exportModuleHelp(
526 const ICommandLineModule &module,
527 const std::string &tag,
528 const std::string &displayName) override;
529 void finishModuleExport() override;
531 void startModuleGroupExport() override;
532 void exportModuleGroup(const char *title,
533 const ModuleGroupContents &modules) override;
534 void finishModuleGroupExport() override;
536 void exportTopic(const IHelpTopic &topic) override;
538 private:
539 IFileOutputRedirector *outputRedirector_;
540 const std::string &binaryName_; //NOLINT(google-runtime-member-string-references)
541 HelpLinks links_;
542 // These never release ownership.
543 std::unique_ptr<TextWriter> indexFile_;
544 std::unique_ptr<TextWriter> manPagesFile_;
547 HelpExportReStructuredText::HelpExportReStructuredText(
548 const CommandLineHelpModuleImpl &helpModule,
549 IFileOutputRedirector *outputRedirector)
550 : outputRedirector_(outputRedirector),
551 binaryName_(helpModule.binaryName_),
552 links_(eHelpOutputFormat_Rst)
554 TextReader linksFile("links.dat");
555 std::string line;
556 linksFile.setTrimTrailingWhiteSpace(true);
557 while (linksFile.readLine(&line))
559 links_.addLink("[REF]." + line + "[ref]",
560 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
561 line);
562 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
564 linksFile.close();
565 initProgramLinks(&links_, helpModule);
568 void HelpExportReStructuredText::startModuleExport()
570 indexFile_ = std::make_unique<TextWriter>(
571 outputRedirector_->openTextOutputFile("fragments/byname.rst"));
572 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
573 binaryName_.c_str(), binaryName_.c_str(),
574 RootHelpText::title));
575 manPagesFile_ = std::make_unique<TextWriter>(
576 outputRedirector_->openTextOutputFile("conf-man.py"));
577 manPagesFile_->writeLine("man_pages = [");
580 void HelpExportReStructuredText::exportModuleHelp(
581 const ICommandLineModule &module,
582 const std::string &tag,
583 const std::string &displayName)
585 TextOutputStreamPointer file
586 = outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
587 TextWriter writer(file);
588 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
589 if (displayName == binaryName_ + " mdrun")
591 // Make an extra link target for the convenience of
592 // MPI-specific documentation
593 writer.writeLine(".. _mdrun_mpi:");
595 writer.ensureEmptyLine();
597 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
598 context.enterSubSection(displayName);
599 context.setModuleDisplayName(displayName);
600 module.writeHelp(context);
602 writer.ensureEmptyLine();
603 writer.writeLine(".. only:: man");
604 writer.writeLine();
605 writer.writeLine(" See also");
606 writer.writeLine(" --------");
607 writer.writeLine();
608 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
609 writer.writeLine();
610 writer.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
611 file->close();
613 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
614 displayName.c_str(), tag.c_str(),
615 module.shortDescription()));
616 manPagesFile_->writeLine(
617 formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
618 tag.c_str(), tag.c_str(), module.shortDescription()));
621 void HelpExportReStructuredText::finishModuleExport()
623 indexFile_->close();
624 indexFile_.reset();
625 // TODO: Generalize.
626 manPagesFile_->writeLine(
627 formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
628 binaryName_.c_str(), binaryName_.c_str(),
629 RootHelpText::title));
630 manPagesFile_->writeLine("]");
631 manPagesFile_->close();
632 manPagesFile_.reset();
635 void HelpExportReStructuredText::startModuleGroupExport()
637 indexFile_ = std::make_unique<TextWriter>(
638 outputRedirector_->openTextOutputFile("fragments/bytopic.rst"));
639 manPagesFile_ = std::make_unique<TextWriter>(
640 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst"));
643 void HelpExportReStructuredText::exportModuleGroup(
644 const char *title,
645 const ModuleGroupContents &modules)
647 indexFile_->ensureEmptyLine();
648 indexFile_->writeLine(title);
649 indexFile_->writeLine(std::string(std::strlen(title), '^'));
650 manPagesFile_->ensureEmptyLine();
651 manPagesFile_->writeLine(title);
652 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
654 ModuleGroupContents::const_iterator module;
655 for (module = modules.begin(); module != modules.end(); ++module)
657 const std::string &tag(module->first);
658 std::string displayName(tag);
659 // TODO: This does not work if the binary name would contain a dash,
660 // but that is not currently the case.
661 const size_t dashPos = displayName.find('-');
662 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
663 "There should always be at least one dash in the tag");
664 displayName[dashPos] = ' ';
665 indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n %s",
666 displayName.c_str(), tag.c_str(),
667 module->second));
668 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s",
669 tag.c_str(),
670 module->second));
674 void HelpExportReStructuredText::finishModuleGroupExport()
676 indexFile_->close();
677 indexFile_.reset();
678 manPagesFile_->close();
679 manPagesFile_.reset();
682 void HelpExportReStructuredText::exportTopic(const IHelpTopic &topic)
684 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
685 TextWriter writer(outputRedirector_->openTextOutputFile(path));
686 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_,
687 binaryName_);
688 HelpManager manager(topic, context.writerContext());
689 manager.writeCurrentTopic();
690 writer.close();
693 /********************************************************************
694 * HelpExportCompletion
697 /*! \internal \brief
698 * Implements export for command-line completion.
700 * \ingroup module_commandline
702 class HelpExportCompletion : public IHelpExport
704 public:
705 //! Initializes completion exporter.
706 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
708 void startModuleExport() override;
709 void exportModuleHelp(
710 const ICommandLineModule &module,
711 const std::string &tag,
712 const std::string &displayName) override;
713 void finishModuleExport() override;
715 void startModuleGroupExport() override {}
716 void exportModuleGroup(const char * /*title*/,
717 const ModuleGroupContents & /*modules*/) override {}
718 void finishModuleGroupExport() override {}
720 void exportTopic(const IHelpTopic & /*topic*/) override {}
722 private:
723 ShellCompletionWriter bashWriter_;
724 std::vector<std::string> modules_;
727 HelpExportCompletion::HelpExportCompletion(
728 const CommandLineHelpModuleImpl &helpModule)
729 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
733 void HelpExportCompletion::startModuleExport()
735 bashWriter_.startCompletions();
738 void HelpExportCompletion::exportModuleHelp(
739 const ICommandLineModule &module,
740 const std::string & /*tag*/,
741 const std::string & /*displayName*/)
743 modules_.emplace_back(module.name());
745 CommandLineHelpContext context(&bashWriter_);
746 // We use the display name to pass the name of the module to the
747 // completion writer.
748 context.setModuleDisplayName(module.name());
749 module.writeHelp(context);
753 void HelpExportCompletion::finishModuleExport()
755 CommandLineCommonOptionsHolder optionsHolder;
756 optionsHolder.initOptions();
757 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
758 bashWriter_.finishCompletions();
761 } // namespace
763 /********************************************************************
764 * CommandLineHelpModuleImpl implementation
767 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
768 const IProgramContext &programContext,
769 const std::string &binaryName,
770 const CommandLineModuleMap &modules,
771 const CommandLineModuleGroupList &groups)
772 : rootTopic_(*this), programContext_(programContext),
773 binaryName_(binaryName), modules_(modules), groups_(groups),
774 context_(nullptr), moduleOverride_(nullptr), bHidden_(false),
775 outputRedirector_(&defaultFileOutputRedirector())
779 std::unique_ptr<IHelpExport>
780 CommandLineHelpModuleImpl::createExporter(const std::string &format,
781 IFileOutputRedirector *redirector)
783 if (format == "rst")
785 return std::unique_ptr<IHelpExport>(
786 new HelpExportReStructuredText(*this, redirector));
788 else if (format == "completion")
790 return std::unique_ptr<IHelpExport>(
791 new HelpExportCompletion(*this));
793 GMX_THROW(NotImplementedError("This help format is not implemented"));
796 void CommandLineHelpModuleImpl::exportHelp(IHelpExport *exporter)
798 // TODO: Would be nicer to have the file names supplied by the build system
799 // and/or export a list of files from here.
800 const char *const program = binaryName_.c_str();
802 exporter->startModuleExport();
803 CommandLineModuleMap::const_iterator module;
804 for (module = modules_.begin(); module != modules_.end(); ++module)
806 if (module->second->shortDescription() != nullptr)
808 const char *const moduleName = module->first.c_str();
809 std::string tag(formatString("%s-%s", program, moduleName));
810 std::string displayName(formatString("%s %s", program, moduleName));
811 exporter->exportModuleHelp(*module->second, tag, displayName);
814 exporter->finishModuleExport();
816 exporter->startModuleGroupExport();
817 CommandLineModuleGroupList::const_iterator group;
818 for (group = groups_.begin(); group != groups_.end(); ++group)
820 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
822 exporter->finishModuleGroupExport();
824 rootTopic_.exportHelp(exporter);
827 namespace
830 /********************************************************************
831 * ModificationCheckingFileOutputStream
834 class ModificationCheckingFileOutputStream : public TextOutputStream
836 public:
837 ModificationCheckingFileOutputStream(
838 const char *path,
839 IFileOutputRedirector *redirector)
840 : path_(path), redirector_(redirector)
844 void write(const char *str) override { contents_.write(str); }
845 void close() override
847 const std::string &newContents = contents_.toString();
848 // TODO: Redirect these for unit tests.
849 if (File::exists(path_, File::returnFalseOnError))
851 const std::string originalContents_
852 = TextReader::readFileToString(path_);
853 if (originalContents_ == newContents)
855 return;
858 TextWriter writer(redirector_->openTextOutputFile(path_));
859 writer.writeString(newContents);
862 private:
863 std::string path_;
864 StringOutputStream contents_;
865 IFileOutputRedirector *redirector_;
868 /********************************************************************
869 * ModificationCheckingFileOutputRedirector
872 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
874 public:
875 explicit ModificationCheckingFileOutputRedirector(
876 IFileOutputRedirector *redirector)
877 : redirector_(redirector)
881 TextOutputStream &standardOutput() override
883 return redirector_->standardOutput();
885 TextOutputStreamPointer openTextOutputFile(const char *filename) override
887 return TextOutputStreamPointer(
888 new ModificationCheckingFileOutputStream(filename, redirector_));
891 private:
892 IFileOutputRedirector *redirector_;
895 } // namespace
897 /********************************************************************
898 * CommandLineHelpModule
901 CommandLineHelpModule::CommandLineHelpModule(
902 const IProgramContext &programContext,
903 const std::string &binaryName,
904 const CommandLineModuleMap &modules,
905 const CommandLineModuleGroupList &groups)
906 : impl_(new Impl(programContext, binaryName, modules, groups))
910 CommandLineHelpModule::~CommandLineHelpModule()
914 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
915 const ICommandLineModule &module) const
917 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
920 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
922 impl_->rootTopic_.addTopic(std::move(topic), bExported);
925 void CommandLineHelpModule::setShowHidden(bool bHidden)
927 impl_->bHidden_ = bHidden;
930 void CommandLineHelpModule::setModuleOverride(
931 const ICommandLineModule &module)
933 impl_->moduleOverride_ = &module;
936 void CommandLineHelpModule::setOutputRedirector(
937 IFileOutputRedirector *output)
939 impl_->outputRedirector_ = output;
942 int CommandLineHelpModule::run(int argc, char *argv[])
944 // Add internal topics lazily here.
945 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
947 const char *const exportFormats[] = { "rst", "completion" };
948 std::string exportFormat;
949 Options options;
950 options.addOption(StringOption("export").store(&exportFormat)
951 .enumValue(exportFormats));
952 CommandLineParser(&options).allowPositionalArguments(true).parse(&argc, argv);
953 if (!exportFormat.empty())
955 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
956 const std::unique_ptr<IHelpExport> exporter(
957 impl_->createExporter(exportFormat, &redirector));
958 impl_->exportHelp(exporter.get());
959 return 0;
962 TextOutputStream &outputFile = impl_->outputRedirector_->standardOutput();
963 TextWriter writer(&outputFile);
964 HelpLinks links(eHelpOutputFormat_Console);
965 initProgramLinks(&links, *impl_);
966 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links,
967 impl_->binaryName_);
968 context.setShowHidden(impl_->bHidden_);
969 if (impl_->moduleOverride_ != nullptr)
971 context.setModuleDisplayName(impl_->programContext_.displayName());
972 impl_->moduleOverride_->writeHelp(context);
973 return 0;
975 impl_->context_ = &context;
977 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
980 for (int i = 1; i < argc; ++i)
982 helpManager.enterTopic(argv[i]);
985 catch (const InvalidInputError &ex)
987 fprintf(stderr, "%s\n", ex.what());
988 return 2;
990 helpManager.writeCurrentTopic();
991 return 0;
994 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
996 const HelpWriterContext &writerContext = context.writerContext();
997 // TODO: Implement.
998 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
1000 return;
1002 writerContext.writeTextBlock(
1003 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
1004 // TODO: More information.
1007 } // namespace gmx