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,2021, 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.
38 * Implements gmx::CommandLineHelpModule.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_commandline
45 #include "cmdlinehelpmodule.h"
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"
82 /********************************************************************
83 * RootHelpTopic declaration
87 * Help topic that forms the root of the help tree for the help subcommand.
89 * \ingroup module_commandline
91 class RootHelpTopic : public AbstractCompositeHelpTopic
95 * Creates a root help topic.
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)
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;
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_;
126 std::vector<std::string> exportedTopics_;
128 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
133 /********************************************************************
134 * CommandLineHelpModuleImpl declaration
137 class CommandLineHelpModuleImpl
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_;
158 IFileOutputRedirector* outputRedirector_;
160 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
166 /********************************************************************
171 * Callbacks for exporting help information for command-line modules.
173 * \ingroup module_commandline
178 //! Shorthand for a list of modules contained in a group.
179 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
181 virtual ~IHelpExport() {}
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
190 virtual void startModuleExport() = 0;
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;
202 * Called after all modules have been exported.
204 * Can close files opened in startModuleExport(), write footers to them
207 virtual void finishModuleExport() = 0;
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;
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;
223 * Called after all module groups have been exported.
225 * Can close files opened in startModuleGroupExport(), write footers to them
228 virtual void finishModuleGroupExport() = 0;
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
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",
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>'.");
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_);
328 return CommandLineHelpContext(context);
332 /********************************************************************
337 * Help topic for listing the commands.
339 * \ingroup module_commandline
341 class CommandsHelpTopic : public IHelpTopic
345 * Creates a command list help topic.
347 * \param[in] helpModule Help module to get module information from.
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;
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)
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 /********************************************************************
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
425 //! Constructs a help topic for a specific module.
426 ModuleHelpTopic(const ICommandLineModule& module, const CommandLineHelpModuleImpl& helpModule) :
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;
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
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
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);
487 * Implements export for web pages as reStructuredText.
489 * \ingroup module_commandline
491 class HelpExportReStructuredText : public IHelpExport
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;
511 IFileOutputRedirector* outputRedirector_;
512 const std::string& binaryName_; //NOLINT(google-runtime-member-string-references)
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");
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()),
533 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
536 initProgramLinks(&links_, helpModule);
539 void HelpExportReStructuredText::startModuleExport()
541 indexFile_ = std::make_unique<TextWriter>(
542 outputRedirector_->openTextOutputFile("fragments/byname.rst"));
543 indexFile_->writeLine(formatString(
544 "* :doc:`%s </onlinehelp/%s>` - %s", binaryName_.c_str(), binaryName_.c_str(), RootHelpText::title));
546 std::make_unique<TextWriter>(outputRedirector_->openTextOutputFile("conf-man.py"));
547 manPagesFile_->writeLine("man_pages = [");
550 void HelpExportReStructuredText::exportModuleHelp(const ICommandLineModule& module,
551 const std::string& tag,
552 const std::string& displayName)
554 TextOutputStreamPointer file =
555 outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
556 TextWriter writer(file);
557 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
558 writer.ensureEmptyLine();
560 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
561 context.enterSubSection(displayName);
562 context.setModuleDisplayName(displayName);
563 module.writeHelp(context);
565 writer.ensureEmptyLine();
566 writer.writeLine(".. only:: man");
568 writer.writeLine(" See also");
569 writer.writeLine(" --------");
571 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
574 " More information about |Gromacs| is available at <http://www.gromacs.org/>.");
577 indexFile_->writeLine(formatString(
578 "* :doc:`%s </onlinehelp/%s>` - %s", displayName.c_str(), tag.c_str(), module.shortDescription()));
579 manPagesFile_->writeLine(formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
582 module.shortDescription()));
585 void HelpExportReStructuredText::finishModuleExport()
590 manPagesFile_->writeLine(formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
593 RootHelpText::title));
594 manPagesFile_->writeLine("]");
595 manPagesFile_->close();
596 manPagesFile_.reset();
599 void HelpExportReStructuredText::startModuleGroupExport()
601 indexFile_ = std::make_unique<TextWriter>(
602 outputRedirector_->openTextOutputFile("fragments/bytopic.rst"));
603 manPagesFile_ = std::make_unique<TextWriter>(
604 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst"));
607 void HelpExportReStructuredText::exportModuleGroup(const char* title, const ModuleGroupContents& modules)
609 indexFile_->ensureEmptyLine();
610 indexFile_->writeLine(title);
611 indexFile_->writeLine(std::string(std::strlen(title), '^'));
612 manPagesFile_->ensureEmptyLine();
613 manPagesFile_->writeLine(title);
614 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
616 ModuleGroupContents::const_iterator module;
617 for (module = modules.begin(); module != modules.end(); ++module)
619 const std::string& tag(module->first);
620 std::string displayName(tag);
621 // TODO: This does not work if the binary name would contain a dash,
622 // but that is not currently the case.
623 const size_t dashPos = displayName.find('-');
624 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
625 "There should always be at least one dash in the tag");
626 displayName[dashPos] = ' ';
627 indexFile_->writeLine(formatString(
628 ":doc:`%s </onlinehelp/%s>`\n %s", displayName.c_str(), tag.c_str(), module->second));
629 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s", tag.c_str(), module->second));
633 void HelpExportReStructuredText::finishModuleGroupExport()
637 manPagesFile_->close();
638 manPagesFile_.reset();
641 void HelpExportReStructuredText::exportTopic(const IHelpTopic& topic)
643 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
644 TextWriter writer(outputRedirector_->openTextOutputFile(path));
645 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
646 HelpManager manager(topic, context.writerContext());
647 manager.writeCurrentTopic();
651 /********************************************************************
652 * HelpExportCompletion
656 * Implements export for command-line completion.
658 * \ingroup module_commandline
660 class HelpExportCompletion : public IHelpExport
663 //! Initializes completion exporter.
664 explicit HelpExportCompletion(const CommandLineHelpModuleImpl& helpModule);
666 void startModuleExport() override;
667 void exportModuleHelp(const ICommandLineModule& module,
668 const std::string& tag,
669 const std::string& displayName) override;
670 void finishModuleExport() override;
672 void startModuleGroupExport() override {}
673 void exportModuleGroup(const char* /*title*/, const ModuleGroupContents& /*modules*/) override
676 void finishModuleGroupExport() override {}
678 void exportTopic(const IHelpTopic& /*topic*/) override {}
681 ShellCompletionWriter bashWriter_;
682 std::vector<std::string> modules_;
685 HelpExportCompletion::HelpExportCompletion(const CommandLineHelpModuleImpl& helpModule) :
686 bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
690 void HelpExportCompletion::startModuleExport()
692 bashWriter_.startCompletions();
695 void HelpExportCompletion::exportModuleHelp(const ICommandLineModule& module,
696 const std::string& /*tag*/,
697 const std::string& /*displayName*/)
699 modules_.emplace_back(module.name());
701 CommandLineHelpContext context(&bashWriter_);
702 // We use the display name to pass the name of the module to the
703 // completion writer.
704 context.setModuleDisplayName(module.name());
705 module.writeHelp(context);
709 void HelpExportCompletion::finishModuleExport()
711 CommandLineCommonOptionsHolder optionsHolder;
712 optionsHolder.initOptions();
713 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
714 bashWriter_.finishCompletions();
719 /********************************************************************
720 * CommandLineHelpModuleImpl implementation
723 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(const IProgramContext& programContext,
724 const std::string& binaryName,
725 const CommandLineModuleMap& modules,
726 const CommandLineModuleGroupList& groups) :
728 programContext_(programContext),
729 binaryName_(binaryName),
733 moduleOverride_(nullptr),
735 outputRedirector_(&defaultFileOutputRedirector())
739 std::unique_ptr<IHelpExport> CommandLineHelpModuleImpl::createExporter(const std::string& format,
740 IFileOutputRedirector* redirector)
744 return std::unique_ptr<IHelpExport>(new HelpExportReStructuredText(*this, redirector));
746 else if (format == "completion")
748 return std::unique_ptr<IHelpExport>(new HelpExportCompletion(*this));
750 GMX_THROW(NotImplementedError("This help format is not implemented"));
753 void CommandLineHelpModuleImpl::exportHelp(IHelpExport* exporter)
755 // TODO: Would be nicer to have the file names supplied by the build system
756 // and/or export a list of files from here.
757 const char* const program = binaryName_.c_str();
759 exporter->startModuleExport();
760 CommandLineModuleMap::const_iterator module;
761 for (module = modules_.begin(); module != modules_.end(); ++module)
763 if (module->second->shortDescription() != nullptr)
765 const char* const moduleName = module->first.c_str();
766 std::string tag(formatString("%s-%s", program, moduleName));
767 std::string displayName(formatString("%s %s", program, moduleName));
768 exporter->exportModuleHelp(*module->second, tag, displayName);
771 exporter->finishModuleExport();
773 exporter->startModuleGroupExport();
774 CommandLineModuleGroupList::const_iterator group;
775 for (group = groups_.begin(); group != groups_.end(); ++group)
777 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
779 exporter->finishModuleGroupExport();
781 rootTopic_.exportHelp(exporter);
787 /********************************************************************
788 * ModificationCheckingFileOutputStream
791 class ModificationCheckingFileOutputStream : public TextOutputStream
794 ModificationCheckingFileOutputStream(const char* path, IFileOutputRedirector* redirector) :
796 redirector_(redirector)
800 void write(const char* str) override { contents_.write(str); }
801 void close() override
803 const std::string& newContents = contents_.toString();
804 // TODO: Redirect these for unit tests.
805 if (File::exists(path_, File::returnFalseOnError))
807 const std::string originalContents_ = TextReader::readFileToString(path_);
808 if (originalContents_ == newContents)
813 TextWriter writer(redirector_->openTextOutputFile(path_));
814 writer.writeString(newContents);
819 StringOutputStream contents_;
820 IFileOutputRedirector* redirector_;
823 /********************************************************************
824 * ModificationCheckingFileOutputRedirector
827 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
830 explicit ModificationCheckingFileOutputRedirector(IFileOutputRedirector* redirector) :
831 redirector_(redirector)
835 TextOutputStream& standardOutput() override { return redirector_->standardOutput(); }
836 TextOutputStreamPointer openTextOutputFile(const char* filename) override
838 return TextOutputStreamPointer(new ModificationCheckingFileOutputStream(filename, redirector_));
842 IFileOutputRedirector* redirector_;
847 /********************************************************************
848 * CommandLineHelpModule
851 CommandLineHelpModule::CommandLineHelpModule(const IProgramContext& programContext,
852 const std::string& binaryName,
853 const CommandLineModuleMap& modules,
854 const CommandLineModuleGroupList& groups) :
855 impl_(new Impl(programContext, binaryName, modules, groups))
859 CommandLineHelpModule::~CommandLineHelpModule() {}
861 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(const ICommandLineModule& module) const
863 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
866 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
868 impl_->rootTopic_.addTopic(std::move(topic), bExported);
871 void CommandLineHelpModule::setShowHidden(bool bHidden)
873 impl_->bHidden_ = bHidden;
876 void CommandLineHelpModule::setModuleOverride(const ICommandLineModule& module)
878 impl_->moduleOverride_ = &module;
881 void CommandLineHelpModule::setOutputRedirector(IFileOutputRedirector* output)
883 impl_->outputRedirector_ = output;
886 int CommandLineHelpModule::run(int argc, char* argv[])
888 // Add internal topics lazily here.
889 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
891 const char* const exportFormats[] = { "rst", "completion" };
892 std::string exportFormat;
894 options.addOption(StringOption("export").store(&exportFormat).enumValue(exportFormats));
895 CommandLineParser(&options).allowPositionalArguments(true).parse(&argc, argv);
896 if (!exportFormat.empty())
898 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
899 const std::unique_ptr<IHelpExport> exporter(impl_->createExporter(exportFormat, &redirector));
900 impl_->exportHelp(exporter.get());
904 TextOutputStream& outputFile = impl_->outputRedirector_->standardOutput();
905 TextWriter writer(&outputFile);
906 HelpLinks links(eHelpOutputFormat_Console);
907 initProgramLinks(&links, *impl_);
908 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links, impl_->binaryName_);
909 context.setShowHidden(impl_->bHidden_);
910 if (impl_->moduleOverride_ != nullptr)
912 context.setModuleDisplayName(impl_->programContext_.displayName());
913 impl_->moduleOverride_->writeHelp(context);
916 impl_->context_ = &context;
918 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
921 for (int i = 1; i < argc; ++i)
923 helpManager.enterTopic(argv[i]);
926 catch (const InvalidInputError& ex)
928 fprintf(stderr, "%s\n", ex.what());
931 helpManager.writeCurrentTopic();
935 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext& context) const
937 const HelpWriterContext& writerContext = context.writerContext();
939 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
943 writerContext.writeTextBlock("Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
944 // TODO: More information.