Update instructions in containers.rst
[gromacs.git] / src / gromacs / commandline / cmdlinehelpmodule.cpp
blob95b4acfa61fe15c5591075a849b0f77e7338b12c
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5 * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \internal \file
37 * \brief
38 * Implements gmx::CommandLineHelpModule.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_commandline
43 #include "gmxpre.h"
45 #include "cmdlinehelpmodule.h"
47 #include <memory>
48 #include <string>
49 #include <vector>
51 #include "gromacs/commandline/cmdlinehelpcontext.h"
52 #include "gromacs/commandline/cmdlinehelpwriter.h"
53 #include "gromacs/commandline/cmdlineparser.h"
54 #include "gromacs/onlinehelp/helpformat.h"
55 #include "gromacs/onlinehelp/helpmanager.h"
56 #include "gromacs/onlinehelp/helptopic.h"
57 #include "gromacs/onlinehelp/helpwritercontext.h"
58 #include "gromacs/options/basicoptions.h"
59 #include "gromacs/options/options.h"
60 #include "gromacs/utility/arrayref.h"
61 #include "gromacs/utility/baseversion.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/fileredirector.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/path.h"
66 #include "gromacs/utility/programcontext.h"
67 #include "gromacs/utility/stringstream.h"
68 #include "gromacs/utility/stringutil.h"
69 #include "gromacs/utility/textreader.h"
70 #include "gromacs/utility/textstream.h"
71 #include "gromacs/utility/textwriter.h"
73 #include "shellcompletions.h"
75 namespace gmx
78 namespace
80 class IHelpExport;
82 /********************************************************************
83 * RootHelpTopic declaration
86 /*! \brief
87 * Help topic that forms the root of the help tree for the help subcommand.
89 * \ingroup module_commandline
91 class RootHelpTopic : public AbstractCompositeHelpTopic
93 public:
94 /*! \brief
95 * Creates a root help topic.
97 * Does not throw.
99 explicit RootHelpTopic(const CommandLineHelpModuleImpl& helpModule) : helpModule_(helpModule) {}
101 const char* name() const override;
102 const char* title() const override { return title_.c_str(); }
104 //! Adds a top-level topic and optionally marks it as exported.
105 void addTopic(HelpTopicPointer topic, bool bExported)
107 if (bExported)
109 exportedTopics_.emplace_back(topic->name());
111 addSubTopic(std::move(topic));
113 //! Exports all the top-level topics with the given exporter.
114 void exportHelp(IHelpExport* exporter);
116 void writeHelp(const HelpWriterContext& context) const override;
118 private:
119 // unused because of the writeHelp() override
120 std::string helpText() const override { return ""; }
122 CommandLineHelpContext createContext(const HelpWriterContext& context) const;
124 const CommandLineHelpModuleImpl& helpModule_;
125 std::string title_;
126 std::vector<std::string> exportedTopics_;
128 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
131 } // namespace
133 /********************************************************************
134 * CommandLineHelpModuleImpl declaration
137 class CommandLineHelpModuleImpl
139 public:
140 CommandLineHelpModuleImpl(const IProgramContext& programContext,
141 const std::string& binaryName,
142 const CommandLineModuleMap& modules,
143 const CommandLineModuleGroupList& groups);
145 std::unique_ptr<IHelpExport> createExporter(const std::string& format, IFileOutputRedirector* redirector);
146 void exportHelp(IHelpExport* exporter);
148 RootHelpTopic rootTopic_;
149 const IProgramContext& programContext_;
150 std::string binaryName_;
151 const CommandLineModuleMap& modules_;
152 const CommandLineModuleGroupList& groups_;
154 CommandLineHelpContext* context_;
155 const ICommandLineModule* moduleOverride_;
156 bool bHidden_;
158 IFileOutputRedirector* outputRedirector_;
160 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
163 namespace
166 /********************************************************************
167 * IHelpExport
170 /*! \brief
171 * Callbacks for exporting help information for command-line modules.
173 * \ingroup module_commandline
175 class IHelpExport
177 public:
178 //! Shorthand for a list of modules contained in a group.
179 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
181 virtual ~IHelpExport() {}
183 /*! \brief
184 * Called once before exporting individual modules.
186 * Can, e.g., open shared output files (e.g., if the output is written
187 * into a single file, or if a separate index is required) and write
188 * headers into them.
190 virtual void startModuleExport() = 0;
191 /*! \brief
192 * Called to export the help for each module.
194 * \param[in] module Module for which the help should be exported.
195 * \param[in] tag Unique tag for the module (gmx-something).
196 * \param[in] displayName Display name for the module (gmx something).
198 virtual void exportModuleHelp(const ICommandLineModule& module,
199 const std::string& tag,
200 const std::string& displayName) = 0;
201 /*! \brief
202 * Called after all modules have been exported.
204 * Can close files opened in startModuleExport(), write footers to them
205 * etc.
207 virtual void finishModuleExport() = 0;
209 /*! \brief
210 * Called once before exporting module groups.
212 * Can, e.g., open a single output file for listing all the groups.
214 virtual void startModuleGroupExport() = 0;
215 /*! \brief
216 * Called to export the help for each module group.
218 * \param[in] title Title for the group.
219 * \param[in] modules List of modules in the group.
221 virtual void exportModuleGroup(const char* title, const ModuleGroupContents& modules) = 0;
222 /*! \brief
223 * Called after all module groups have been exported.
225 * Can close files opened in startModuleGroupExport(), write footers to them
226 * etc.
228 virtual void finishModuleGroupExport() = 0;
230 /*! \brief
231 * Called to export the help for a top-level topic.
233 * \param[in] topic Topic to export.
235 virtual void exportTopic(const IHelpTopic& topic) = 0;
238 /********************************************************************
239 * RootHelpTopic implementation
242 struct RootHelpText
244 static const char title[];
245 static const char* const text[];
248 // These are used for the gmx.1 man page.
249 // TODO: Do not hardcode them here, but pass them from the outside to make this
250 // code more generic.
251 const char RootHelpText::title[] = "molecular dynamics simulation suite";
252 const char* const RootHelpText::text[] = {
253 "|Gromacs| is a full-featured suite of programs to perform molecular",
254 "dynamics simulations, i.e., to simulate the behavior of systems with",
255 "hundreds to millions of particles using Newtonian equations of motion.",
256 "It is primarily used for research on proteins, lipids, and polymers, but",
257 "can be applied to a wide variety of chemical and biological research",
258 "questions.",
261 const char* RootHelpTopic::name() const
263 return helpModule_.binaryName_.c_str();
266 void RootHelpTopic::exportHelp(IHelpExport* exporter)
268 std::vector<std::string>::const_iterator topicName;
269 for (topicName = exportedTopics_.begin(); topicName != exportedTopics_.end(); ++topicName)
271 const IHelpTopic* topic = findSubTopic(topicName->c_str());
272 GMX_RELEASE_ASSERT(topic != nullptr, "Exported help topic no longer found");
273 exporter->exportTopic(*topic);
275 // For now, the title is only set for the export to make it not appear in
276 // console output, which makes things consistent for 'gmx help' and
277 // 'gmx help <command>'.
278 title_ = RootHelpText::title;
279 exporter->exportTopic(*this);
282 void RootHelpTopic::writeHelp(const HelpWriterContext& context) const
285 CommandLineCommonOptionsHolder optionsHolder;
286 CommandLineHelpContext cmdlineContext(createContext(context));
287 cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
288 optionsHolder.initOptions();
289 Options& options = *optionsHolder.options();
290 ArrayRef<const char* const> helpText;
291 if (context.outputFormat() != eHelpOutputFormat_Console)
293 helpText = RootHelpText::text;
295 // TODO: Add <command> [<args>] into the synopsis.
296 CommandLineHelpWriter(options).setHelpText(helpText).writeHelp(cmdlineContext);
298 if (context.outputFormat() == eHelpOutputFormat_Console)
300 // TODO: Consider printing a list of "core" commands. Would require someone
301 // to determine such a set...
302 context.paragraphBreak();
303 writeSubTopicList(context, "Additional help is available on the following topics:");
304 context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
305 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
307 else
309 // TODO: This should not really end up on the HTML page.
310 context.writeTitle(formatString("%s commands", helpModule_.binaryName_.c_str()));
311 context.writeTextBlock(
312 "The following commands are available. Please refer to their "
313 "individual man pages or [TT][PROGRAM] help <command>[tt] "
314 "for further details.");
315 context.writeTextBlock("");
316 context.writeTextBlock(".. include:: /fragments/bytopic-man.rst");
320 CommandLineHelpContext RootHelpTopic::createContext(const HelpWriterContext& context) const
322 if (helpModule_.context_ != nullptr)
324 return CommandLineHelpContext(*helpModule_.context_);
326 else
328 return CommandLineHelpContext(context);
332 /********************************************************************
333 * CommandsHelpTopic
336 /*! \brief
337 * Help topic for listing the commands.
339 * \ingroup module_commandline
341 class CommandsHelpTopic : public IHelpTopic
343 public:
344 /*! \brief
345 * Creates a command list help topic.
347 * \param[in] helpModule Help module to get module information from.
349 * Does not throw.
351 explicit CommandsHelpTopic(const CommandLineHelpModuleImpl& helpModule) :
352 helpModule_(helpModule)
356 const char* name() const override { return "commands"; }
357 const char* title() const override { return "List of available commands"; }
358 bool hasSubTopics() const override { return false; }
359 const IHelpTopic* findSubTopic(const char* /*name*/) const override { return nullptr; }
361 void writeHelp(const HelpWriterContext& context) const override;
363 private:
364 const CommandLineHelpModuleImpl& helpModule_;
366 GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
369 void CommandsHelpTopic::writeHelp(const HelpWriterContext& context) const
371 if (context.outputFormat() != eHelpOutputFormat_Console)
373 GMX_THROW(NotImplementedError("Module list is not implemented for this output format"));
375 int maxNameLength = 0;
376 const CommandLineModuleMap& modules = helpModule_.modules_;
377 CommandLineModuleMap::const_iterator module;
378 for (module = modules.begin(); module != modules.end(); ++module)
380 int nameLength = static_cast<int>(module->first.length());
381 if (module->second->shortDescription() != nullptr && nameLength > maxNameLength)
383 maxNameLength = nameLength;
386 context.writeTextBlock(
387 "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
388 "Available commands:");
389 TextWriter& file = context.outputFile();
390 TextTableFormatter formatter;
391 formatter.addColumn(nullptr, maxNameLength + 1, false);
392 formatter.addColumn(nullptr, 72 - maxNameLength, true);
393 formatter.setFirstColumnIndent(4);
394 for (module = modules.begin(); module != modules.end(); ++module)
396 const char* name = module->first.c_str();
397 const char* description = module->second->shortDescription();
398 if (description != nullptr)
400 formatter.clear();
401 formatter.addColumnLine(0, name);
402 formatter.addColumnLine(1, description);
403 file.writeString(formatter.formatRow());
406 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
409 /********************************************************************
410 * ModuleHelpTopic
413 /*! \brief
414 * Help topic wrapper for a command-line module.
416 * This class implements IHelpTopic such that it wraps a
417 * ICommandLineModule, allowing subcommand "help <command>"
418 * to produce the help for "<command>".
420 * \ingroup module_commandline
422 class ModuleHelpTopic : public IHelpTopic
424 public:
425 //! Constructs a help topic for a specific module.
426 ModuleHelpTopic(const ICommandLineModule& module, const CommandLineHelpModuleImpl& helpModule) :
427 module_(module),
428 helpModule_(helpModule)
432 const char* name() const override { return module_.name(); }
433 const char* title() const override { return nullptr; }
434 bool hasSubTopics() const override { return false; }
435 const IHelpTopic* findSubTopic(const char* /*name*/) const override { return nullptr; }
436 void writeHelp(const HelpWriterContext& context) const override;
438 private:
439 const ICommandLineModule& module_;
440 const CommandLineHelpModuleImpl& helpModule_;
442 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
445 void ModuleHelpTopic::writeHelp(const HelpWriterContext& /*context*/) const
447 CommandLineHelpContext context(*helpModule_.context_);
448 const char* const program = helpModule_.binaryName_.c_str();
449 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
450 module_.writeHelp(context);
453 /********************************************************************
454 * HelpExportReStructuredText
457 /*! \internal \brief
458 * Adds hyperlinks to modules within this binary.
460 * \param[in,out] links Links are added here.
461 * \param[in] helpModule Help module to get module information from.
462 * \throws std::bad_alloc if out of memory.
464 * Initializes a HelpLinks object with links to modules defined in
465 * \p helpModule.
467 * \ingroup module_commandline
469 void initProgramLinks(HelpLinks* links, const CommandLineHelpModuleImpl& helpModule)
471 const char* const program = helpModule.binaryName_.c_str();
472 CommandLineModuleMap::const_iterator module;
473 for (module = helpModule.modules_.begin(); module != helpModule.modules_.end(); ++module)
475 if (module->second->shortDescription() != nullptr)
477 std::string linkName("[gmx-" + module->first + "]");
478 const char* name = module->first.c_str();
479 std::string reference(formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
480 std::string displayName(formatString("[TT]%s %s[tt]", program, name));
481 links->addLink(linkName, reference, displayName);
486 /*! \internal \brief
487 * Implements export for web pages as reStructuredText.
489 * \ingroup module_commandline
491 class HelpExportReStructuredText : public IHelpExport
493 public:
494 //! Initializes reST exporter.
495 HelpExportReStructuredText(const CommandLineHelpModuleImpl& helpModule,
496 IFileOutputRedirector* outputRedirector);
498 void startModuleExport() override;
499 void exportModuleHelp(const ICommandLineModule& module,
500 const std::string& tag,
501 const std::string& displayName) override;
502 void finishModuleExport() override;
504 void startModuleGroupExport() override;
505 void exportModuleGroup(const char* title, const ModuleGroupContents& modules) override;
506 void finishModuleGroupExport() override;
508 void exportTopic(const IHelpTopic& topic) override;
510 private:
511 IFileOutputRedirector* outputRedirector_;
512 const std::string& binaryName_; //NOLINT(google-runtime-member-string-references)
513 HelpLinks links_;
514 // These never release ownership.
515 std::unique_ptr<TextWriter> indexFile_;
516 std::unique_ptr<TextWriter> manPagesFile_;
519 HelpExportReStructuredText::HelpExportReStructuredText(const CommandLineHelpModuleImpl& helpModule,
520 IFileOutputRedirector* outputRedirector) :
521 outputRedirector_(outputRedirector),
522 binaryName_(helpModule.binaryName_),
523 links_(eHelpOutputFormat_Rst)
525 TextReader linksFile("links.dat");
526 std::string line;
527 linksFile.setTrimTrailingWhiteSpace(true);
528 while (linksFile.readLine(&line))
530 links_.addLink("[REF]." + line + "[ref]",
531 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()), line);
532 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
534 linksFile.close();
535 initProgramLinks(&links_, helpModule);
538 void HelpExportReStructuredText::startModuleExport()
540 indexFile_ = std::make_unique<TextWriter>(
541 outputRedirector_->openTextOutputFile("fragments/byname.rst"));
542 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s", binaryName_.c_str(),
543 binaryName_.c_str(), RootHelpText::title));
544 manPagesFile_ =
545 std::make_unique<TextWriter>(outputRedirector_->openTextOutputFile("conf-man.py"));
546 manPagesFile_->writeLine("man_pages = [");
549 void HelpExportReStructuredText::exportModuleHelp(const ICommandLineModule& module,
550 const std::string& tag,
551 const std::string& displayName)
553 TextOutputStreamPointer file =
554 outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
555 TextWriter writer(file);
556 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
557 if (displayName == binaryName_ + " mdrun")
559 // Make an extra link target for the convenience of
560 // MPI-specific documentation
561 writer.writeLine(".. _mdrun_mpi:");
563 writer.ensureEmptyLine();
565 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
566 context.enterSubSection(displayName);
567 context.setModuleDisplayName(displayName);
568 module.writeHelp(context);
570 writer.ensureEmptyLine();
571 writer.writeLine(".. only:: man");
572 writer.writeLine();
573 writer.writeLine(" See also");
574 writer.writeLine(" --------");
575 writer.writeLine();
576 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
577 writer.writeLine();
578 writer.writeLine(
579 " More information about |Gromacs| is available at <http://www.gromacs.org/>.");
580 file->close();
582 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s", displayName.c_str(),
583 tag.c_str(), module.shortDescription()));
584 manPagesFile_->writeLine(formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
585 tag.c_str(), tag.c_str(), module.shortDescription()));
588 void HelpExportReStructuredText::finishModuleExport()
590 indexFile_->close();
591 indexFile_.reset();
592 // TODO: Generalize.
593 manPagesFile_->writeLine(formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
594 binaryName_.c_str(), binaryName_.c_str(), RootHelpText::title));
595 manPagesFile_->writeLine("]");
596 manPagesFile_->close();
597 manPagesFile_.reset();
600 void HelpExportReStructuredText::startModuleGroupExport()
602 indexFile_ = std::make_unique<TextWriter>(
603 outputRedirector_->openTextOutputFile("fragments/bytopic.rst"));
604 manPagesFile_ = std::make_unique<TextWriter>(
605 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst"));
608 void HelpExportReStructuredText::exportModuleGroup(const char* title, const ModuleGroupContents& modules)
610 indexFile_->ensureEmptyLine();
611 indexFile_->writeLine(title);
612 indexFile_->writeLine(std::string(std::strlen(title), '^'));
613 manPagesFile_->ensureEmptyLine();
614 manPagesFile_->writeLine(title);
615 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
617 ModuleGroupContents::const_iterator module;
618 for (module = modules.begin(); module != modules.end(); ++module)
620 const std::string& tag(module->first);
621 std::string displayName(tag);
622 // TODO: This does not work if the binary name would contain a dash,
623 // but that is not currently the case.
624 const size_t dashPos = displayName.find('-');
625 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
626 "There should always be at least one dash in the tag");
627 displayName[dashPos] = ' ';
628 indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n %s", displayName.c_str(),
629 tag.c_str(), module->second));
630 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s", tag.c_str(), module->second));
634 void HelpExportReStructuredText::finishModuleGroupExport()
636 indexFile_->close();
637 indexFile_.reset();
638 manPagesFile_->close();
639 manPagesFile_.reset();
642 void HelpExportReStructuredText::exportTopic(const IHelpTopic& topic)
644 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
645 TextWriter writer(outputRedirector_->openTextOutputFile(path));
646 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
647 HelpManager manager(topic, context.writerContext());
648 manager.writeCurrentTopic();
649 writer.close();
652 /********************************************************************
653 * HelpExportCompletion
656 /*! \internal \brief
657 * Implements export for command-line completion.
659 * \ingroup module_commandline
661 class HelpExportCompletion : public IHelpExport
663 public:
664 //! Initializes completion exporter.
665 explicit HelpExportCompletion(const CommandLineHelpModuleImpl& helpModule);
667 void startModuleExport() override;
668 void exportModuleHelp(const ICommandLineModule& module,
669 const std::string& tag,
670 const std::string& displayName) override;
671 void finishModuleExport() override;
673 void startModuleGroupExport() override {}
674 void exportModuleGroup(const char* /*title*/, const ModuleGroupContents& /*modules*/) override
677 void finishModuleGroupExport() override {}
679 void exportTopic(const IHelpTopic& /*topic*/) override {}
681 private:
682 ShellCompletionWriter bashWriter_;
683 std::vector<std::string> modules_;
686 HelpExportCompletion::HelpExportCompletion(const CommandLineHelpModuleImpl& helpModule) :
687 bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
691 void HelpExportCompletion::startModuleExport()
693 bashWriter_.startCompletions();
696 void HelpExportCompletion::exportModuleHelp(const ICommandLineModule& module,
697 const std::string& /*tag*/,
698 const std::string& /*displayName*/)
700 modules_.emplace_back(module.name());
702 CommandLineHelpContext context(&bashWriter_);
703 // We use the display name to pass the name of the module to the
704 // completion writer.
705 context.setModuleDisplayName(module.name());
706 module.writeHelp(context);
710 void HelpExportCompletion::finishModuleExport()
712 CommandLineCommonOptionsHolder optionsHolder;
713 optionsHolder.initOptions();
714 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
715 bashWriter_.finishCompletions();
718 } // namespace
720 /********************************************************************
721 * CommandLineHelpModuleImpl implementation
724 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(const IProgramContext& programContext,
725 const std::string& binaryName,
726 const CommandLineModuleMap& modules,
727 const CommandLineModuleGroupList& groups) :
728 rootTopic_(*this),
729 programContext_(programContext),
730 binaryName_(binaryName),
731 modules_(modules),
732 groups_(groups),
733 context_(nullptr),
734 moduleOverride_(nullptr),
735 bHidden_(false),
736 outputRedirector_(&defaultFileOutputRedirector())
740 std::unique_ptr<IHelpExport> CommandLineHelpModuleImpl::createExporter(const std::string& format,
741 IFileOutputRedirector* redirector)
743 if (format == "rst")
745 return std::unique_ptr<IHelpExport>(new HelpExportReStructuredText(*this, redirector));
747 else if (format == "completion")
749 return std::unique_ptr<IHelpExport>(new HelpExportCompletion(*this));
751 GMX_THROW(NotImplementedError("This help format is not implemented"));
754 void CommandLineHelpModuleImpl::exportHelp(IHelpExport* exporter)
756 // TODO: Would be nicer to have the file names supplied by the build system
757 // and/or export a list of files from here.
758 const char* const program = binaryName_.c_str();
760 exporter->startModuleExport();
761 CommandLineModuleMap::const_iterator module;
762 for (module = modules_.begin(); module != modules_.end(); ++module)
764 if (module->second->shortDescription() != nullptr)
766 const char* const moduleName = module->first.c_str();
767 std::string tag(formatString("%s-%s", program, moduleName));
768 std::string displayName(formatString("%s %s", program, moduleName));
769 exporter->exportModuleHelp(*module->second, tag, displayName);
772 exporter->finishModuleExport();
774 exporter->startModuleGroupExport();
775 CommandLineModuleGroupList::const_iterator group;
776 for (group = groups_.begin(); group != groups_.end(); ++group)
778 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
780 exporter->finishModuleGroupExport();
782 rootTopic_.exportHelp(exporter);
785 namespace
788 /********************************************************************
789 * ModificationCheckingFileOutputStream
792 class ModificationCheckingFileOutputStream : public TextOutputStream
794 public:
795 ModificationCheckingFileOutputStream(const char* path, IFileOutputRedirector* redirector) :
796 path_(path),
797 redirector_(redirector)
801 void write(const char* str) override { contents_.write(str); }
802 void close() override
804 const std::string& newContents = contents_.toString();
805 // TODO: Redirect these for unit tests.
806 if (File::exists(path_, File::returnFalseOnError))
808 const std::string originalContents_ = TextReader::readFileToString(path_);
809 if (originalContents_ == newContents)
811 return;
814 TextWriter writer(redirector_->openTextOutputFile(path_));
815 writer.writeString(newContents);
818 private:
819 std::string path_;
820 StringOutputStream contents_;
821 IFileOutputRedirector* redirector_;
824 /********************************************************************
825 * ModificationCheckingFileOutputRedirector
828 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
830 public:
831 explicit ModificationCheckingFileOutputRedirector(IFileOutputRedirector* redirector) :
832 redirector_(redirector)
836 TextOutputStream& standardOutput() override { return redirector_->standardOutput(); }
837 TextOutputStreamPointer openTextOutputFile(const char* filename) override
839 return TextOutputStreamPointer(new ModificationCheckingFileOutputStream(filename, redirector_));
842 private:
843 IFileOutputRedirector* redirector_;
846 } // namespace
848 /********************************************************************
849 * CommandLineHelpModule
852 CommandLineHelpModule::CommandLineHelpModule(const IProgramContext& programContext,
853 const std::string& binaryName,
854 const CommandLineModuleMap& modules,
855 const CommandLineModuleGroupList& groups) :
856 impl_(new Impl(programContext, binaryName, modules, groups))
860 CommandLineHelpModule::~CommandLineHelpModule() {}
862 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(const ICommandLineModule& module) const
864 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
867 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
869 impl_->rootTopic_.addTopic(std::move(topic), bExported);
872 void CommandLineHelpModule::setShowHidden(bool bHidden)
874 impl_->bHidden_ = bHidden;
877 void CommandLineHelpModule::setModuleOverride(const ICommandLineModule& module)
879 impl_->moduleOverride_ = &module;
882 void CommandLineHelpModule::setOutputRedirector(IFileOutputRedirector* output)
884 impl_->outputRedirector_ = output;
887 int CommandLineHelpModule::run(int argc, char* argv[])
889 // Add internal topics lazily here.
890 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
892 const char* const exportFormats[] = { "rst", "completion" };
893 std::string exportFormat;
894 Options options;
895 options.addOption(StringOption("export").store(&exportFormat).enumValue(exportFormats));
896 CommandLineParser(&options).allowPositionalArguments(true).parse(&argc, argv);
897 if (!exportFormat.empty())
899 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
900 const std::unique_ptr<IHelpExport> exporter(impl_->createExporter(exportFormat, &redirector));
901 impl_->exportHelp(exporter.get());
902 return 0;
905 TextOutputStream& outputFile = impl_->outputRedirector_->standardOutput();
906 TextWriter writer(&outputFile);
907 HelpLinks links(eHelpOutputFormat_Console);
908 initProgramLinks(&links, *impl_);
909 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links, impl_->binaryName_);
910 context.setShowHidden(impl_->bHidden_);
911 if (impl_->moduleOverride_ != nullptr)
913 context.setModuleDisplayName(impl_->programContext_.displayName());
914 impl_->moduleOverride_->writeHelp(context);
915 return 0;
917 impl_->context_ = &context;
919 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
922 for (int i = 1; i < argc; ++i)
924 helpManager.enterTopic(argv[i]);
927 catch (const InvalidInputError& ex)
929 fprintf(stderr, "%s\n", ex.what());
930 return 2;
932 helpManager.writeCurrentTopic();
933 return 0;
936 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext& context) const
938 const HelpWriterContext& writerContext = context.writerContext();
939 // TODO: Implement.
940 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
942 return;
944 writerContext.writeTextBlock("Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
945 // TODO: More information.
948 } // namespace gmx