Add coolquotes
[gromacs.git] / src / gromacs / commandline / cmdlinehelpmodule.cpp
blob9716758dfa74ead336ff79f3818036e56ff8e620
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016,2017,2018, 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/compat/make_unique.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)
100 : helpModule_(helpModule)
104 const char *name() const override;
105 const char *title() const override { return title_.c_str(); }
107 //! Adds a top-level topic and optionally marks it as exported.
108 void addTopic(HelpTopicPointer topic, bool bExported)
110 if (bExported)
112 exportedTopics_.emplace_back(topic->name());
114 addSubTopic(std::move(topic));
116 //! Exports all the top-level topics with the given exporter.
117 void exportHelp(IHelpExport *exporter);
119 void writeHelp(const HelpWriterContext &context) const override;
121 private:
122 // unused because of the writeHelp() override
123 std::string helpText() const override { return ""; }
125 CommandLineHelpContext createContext(const HelpWriterContext &context) const;
127 const CommandLineHelpModuleImpl &helpModule_;
128 std::string title_;
129 std::vector<std::string> exportedTopics_;
131 GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
134 } // namespace
136 /********************************************************************
137 * CommandLineHelpModuleImpl declaration
140 class CommandLineHelpModuleImpl
142 public:
143 CommandLineHelpModuleImpl(const IProgramContext &programContext,
144 const std::string &binaryName,
145 const CommandLineModuleMap &modules,
146 const CommandLineModuleGroupList &groups);
148 std::unique_ptr<IHelpExport> createExporter(
149 const std::string &format,
150 IFileOutputRedirector *redirector);
151 void exportHelp(IHelpExport *exporter);
153 RootHelpTopic rootTopic_;
154 const IProgramContext &programContext_;
155 std::string binaryName_;
156 const CommandLineModuleMap &modules_;
157 const CommandLineModuleGroupList &groups_;
159 CommandLineHelpContext *context_;
160 const ICommandLineModule *moduleOverride_;
161 bool bHidden_;
163 IFileOutputRedirector *outputRedirector_;
165 GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
168 namespace
171 /********************************************************************
172 * IHelpExport
175 /*! \brief
176 * Callbacks for exporting help information for command-line modules.
178 * \ingroup module_commandline
180 class IHelpExport
182 public:
183 //! Shorthand for a list of modules contained in a group.
184 typedef CommandLineModuleGroupData::ModuleList ModuleGroupContents;
186 virtual ~IHelpExport() {}
188 /*! \brief
189 * Called once before exporting individual modules.
191 * Can, e.g., open shared output files (e.g., if the output is written
192 * into a single file, or if a separate index is required) and write
193 * headers into them.
195 virtual void startModuleExport() = 0;
196 /*! \brief
197 * Called to export the help for each module.
199 * \param[in] module Module for which the help should be exported.
200 * \param[in] tag Unique tag for the module (gmx-something).
201 * \param[in] displayName Display name for the module (gmx something).
203 virtual void exportModuleHelp(
204 const ICommandLineModule &module,
205 const std::string &tag,
206 const std::string &displayName) = 0;
207 /*! \brief
208 * Called after all modules have been exported.
210 * Can close files opened in startModuleExport(), write footers to them
211 * etc.
213 virtual void finishModuleExport() = 0;
215 /*! \brief
216 * Called once before exporting module groups.
218 * Can, e.g., open a single output file for listing all the groups.
220 virtual void startModuleGroupExport() = 0;
221 /*! \brief
222 * Called to export the help for each module group.
224 * \param[in] title Title for the group.
225 * \param[in] modules List of modules in the group.
227 virtual void exportModuleGroup(const char *title,
228 const ModuleGroupContents &modules) = 0;
229 /*! \brief
230 * Called after all module groups have been exported.
232 * Can close files opened in startModuleGroupExport(), write footers to them
233 * etc.
235 virtual void finishModuleGroupExport() = 0;
237 /*! \brief
238 * Called to export the help for a top-level topic.
240 * \param[in] topic Topic to export.
242 virtual void exportTopic(const IHelpTopic &topic) = 0;
245 /********************************************************************
246 * RootHelpTopic implementation
249 struct RootHelpText
251 static const char title[];
252 static const char *const text[];
255 // These are used for the gmx.1 man page.
256 // TODO: Do not hardcode them here, but pass them from the outside to make this
257 // code more generic.
258 const char RootHelpText::title[] = "molecular dynamics simulation suite";
259 const char *const RootHelpText::text[] = {
260 "|Gromacs| is a full-featured suite of programs to perform molecular",
261 "dynamics simulations, i.e., to simulate the behavior of systems with",
262 "hundreds to millions of particles using Newtonian equations of motion.",
263 "It is primarily used for research on proteins, lipids, and polymers, but",
264 "can be applied to a wide variety of chemical and biological research",
265 "questions.",
268 const char *RootHelpTopic::name() const
270 return helpModule_.binaryName_.c_str();
273 void RootHelpTopic::exportHelp(IHelpExport *exporter)
275 std::vector<std::string>::const_iterator topicName;
276 for (topicName = exportedTopics_.begin();
277 topicName != exportedTopics_.end();
278 ++topicName)
280 const IHelpTopic *topic = findSubTopic(topicName->c_str());
281 GMX_RELEASE_ASSERT(topic != nullptr, "Exported help topic no longer found");
282 exporter->exportTopic(*topic);
284 // For now, the title is only set for the export to make it not appear in
285 // console output, which makes things consistent for 'gmx help' and
286 // 'gmx help <command>'.
287 title_ = RootHelpText::title;
288 exporter->exportTopic(*this);
291 void RootHelpTopic::writeHelp(const HelpWriterContext &context) const
294 CommandLineCommonOptionsHolder optionsHolder;
295 CommandLineHelpContext cmdlineContext(createContext(context));
296 cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
297 optionsHolder.initOptions();
298 Options &options = *optionsHolder.options();
299 ArrayRef<const char *const> helpText;
300 if (context.outputFormat() != eHelpOutputFormat_Console)
302 helpText = RootHelpText::text;
304 // TODO: Add <command> [<args>] into the synopsis.
305 CommandLineHelpWriter(options)
306 .setHelpText(helpText)
307 .writeHelp(cmdlineContext);
309 if (context.outputFormat() == eHelpOutputFormat_Console)
311 // TODO: Consider printing a list of "core" commands. Would require someone
312 // to determine such a set...
313 context.paragraphBreak();
314 writeSubTopicList(context,
315 "Additional help is available on the following topics:");
316 context.writeTextBlock("To access the help, use '[PROGRAM] help <topic>'.");
317 context.writeTextBlock("For help on a command, use '[PROGRAM] help <command>'.");
319 else
321 // TODO: This should not really end up on the HTML page.
322 context.writeTitle(formatString("%s commands", helpModule_.binaryName_.c_str()));
323 context.writeTextBlock(
324 "The following commands are available. Please refer to their "
325 "individual man pages or [TT][PROGRAM] help <command>[tt] "
326 "for further details.");
327 context.writeTextBlock("");
328 context.writeTextBlock(".. include:: /fragments/bytopic-man.rst");
332 CommandLineHelpContext
333 RootHelpTopic::createContext(const HelpWriterContext &context) const
335 if (helpModule_.context_ != nullptr)
337 return CommandLineHelpContext(*helpModule_.context_);
339 else
341 return CommandLineHelpContext(context);
345 /********************************************************************
346 * CommandsHelpTopic
349 /*! \brief
350 * Help topic for listing the commands.
352 * \ingroup module_commandline
354 class CommandsHelpTopic : public IHelpTopic
356 public:
357 /*! \brief
358 * Creates a command list help topic.
360 * \param[in] helpModule Help module to get module information from.
362 * Does not throw.
364 explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
365 : helpModule_(helpModule)
369 const char *name() const override { return "commands"; }
370 const char *title() const override { return "List of available commands"; }
371 bool hasSubTopics() const override { return false; }
372 const IHelpTopic *findSubTopic(const char * /*name*/) const override
374 return nullptr;
377 void writeHelp(const HelpWriterContext &context) const override;
379 private:
380 const CommandLineHelpModuleImpl &helpModule_;
382 GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
385 void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
387 if (context.outputFormat() != eHelpOutputFormat_Console)
389 GMX_THROW(NotImplementedError(
390 "Module list is not implemented for this output format"));
392 int maxNameLength = 0;
393 const CommandLineModuleMap &modules = helpModule_.modules_;
394 CommandLineModuleMap::const_iterator module;
395 for (module = modules.begin(); module != modules.end(); ++module)
397 int nameLength = static_cast<int>(module->first.length());
398 if (module->second->shortDescription() != nullptr
399 && nameLength > maxNameLength)
401 maxNameLength = nameLength;
404 context.writeTextBlock(
405 "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
406 "Available commands:");
407 TextWriter &file = context.outputFile();
408 TextTableFormatter formatter;
409 formatter.addColumn(nullptr, maxNameLength + 1, false);
410 formatter.addColumn(nullptr, 72 - maxNameLength, true);
411 formatter.setFirstColumnIndent(4);
412 for (module = modules.begin(); module != modules.end(); ++module)
414 const char *name = module->first.c_str();
415 const char *description = module->second->shortDescription();
416 if (description != nullptr)
418 formatter.clear();
419 formatter.addColumnLine(0, name);
420 formatter.addColumnLine(1, description);
421 file.writeString(formatter.formatRow());
424 context.writeTextBlock(
425 "For help on a command, use '[PROGRAM] help <command>'.");
428 /********************************************************************
429 * ModuleHelpTopic
432 /*! \brief
433 * Help topic wrapper for a command-line module.
435 * This class implements IHelpTopic such that it wraps a
436 * ICommandLineModule, allowing subcommand "help <command>"
437 * to produce the help for "<command>".
439 * \ingroup module_commandline
441 class ModuleHelpTopic : public IHelpTopic
443 public:
444 //! Constructs a help topic for a specific module.
445 ModuleHelpTopic(const ICommandLineModule &module,
446 const CommandLineHelpModuleImpl &helpModule)
447 : module_(module), helpModule_(helpModule)
451 const char *name() const override { return module_.name(); }
452 const char *title() const override { return nullptr; }
453 bool hasSubTopics() const override { return false; }
454 const IHelpTopic *findSubTopic(const char * /*name*/) const override
456 return nullptr;
458 void writeHelp(const HelpWriterContext &context) const override;
460 private:
461 const ICommandLineModule &module_;
462 const CommandLineHelpModuleImpl &helpModule_;
464 GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
467 void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
469 CommandLineHelpContext context(*helpModule_.context_);
470 const char *const program = helpModule_.binaryName_.c_str();
471 context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
472 module_.writeHelp(context);
475 /********************************************************************
476 * HelpExportReStructuredText
479 /*! \internal \brief
480 * Adds hyperlinks to modules within this binary.
482 * \param[in,out] links Links are added here.
483 * \param[in] helpModule Help module to get module information from.
484 * \throws std::bad_alloc if out of memory.
486 * Initializes a HelpLinks object with links to modules defined in
487 * \p helpModule.
489 * \ingroup module_commandline
491 void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
493 const char *const program = helpModule.binaryName_.c_str();
494 CommandLineModuleMap::const_iterator module;
495 for (module = helpModule.modules_.begin();
496 module != helpModule.modules_.end();
497 ++module)
499 if (module->second->shortDescription() != nullptr)
501 std::string linkName("[gmx-" + module->first + "]");
502 const char *name = module->first.c_str();
503 std::string reference(
504 formatString(":doc:`%s %s <%s-%s>`", program, name, program, name));
505 std::string displayName(
506 formatString("[TT]%s %s[tt]", program, name));
507 links->addLink(linkName, reference, displayName);
512 /*! \internal \brief
513 * Implements export for web pages as reStructuredText.
515 * \ingroup module_commandline
517 class HelpExportReStructuredText : public IHelpExport
519 public:
520 //! Initializes reST exporter.
521 HelpExportReStructuredText(
522 const CommandLineHelpModuleImpl &helpModule,
523 IFileOutputRedirector *outputRedirector);
525 void startModuleExport() override;
526 void exportModuleHelp(
527 const ICommandLineModule &module,
528 const std::string &tag,
529 const std::string &displayName) override;
530 void finishModuleExport() override;
532 void startModuleGroupExport() override;
533 void exportModuleGroup(const char *title,
534 const ModuleGroupContents &modules) override;
535 void finishModuleGroupExport() override;
537 void exportTopic(const IHelpTopic &topic) override;
539 private:
540 IFileOutputRedirector *outputRedirector_;
541 const std::string &binaryName_; //NOLINT(google-runtime-member-string-references)
542 HelpLinks links_;
543 // These never release ownership.
544 std::unique_ptr<TextWriter> indexFile_;
545 std::unique_ptr<TextWriter> manPagesFile_;
548 HelpExportReStructuredText::HelpExportReStructuredText(
549 const CommandLineHelpModuleImpl &helpModule,
550 IFileOutputRedirector *outputRedirector)
551 : outputRedirector_(outputRedirector),
552 binaryName_(helpModule.binaryName_),
553 links_(eHelpOutputFormat_Rst)
555 TextReader linksFile("links.dat");
556 std::string line;
557 linksFile.setTrimTrailingWhiteSpace(true);
558 while (linksFile.readLine(&line))
560 links_.addLink("[REF]." + line + "[ref]",
561 formatString(":ref:`.%s <%s>`", line.c_str(), line.c_str()),
562 line);
563 links_.addLink("[REF]" + line + "[ref]", formatString(":ref:`%s`", line.c_str()), line);
565 linksFile.close();
566 initProgramLinks(&links_, helpModule);
569 void HelpExportReStructuredText::startModuleExport()
571 indexFile_ = compat::make_unique<TextWriter>(
572 outputRedirector_->openTextOutputFile("fragments/byname.rst"));
573 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
574 binaryName_.c_str(), binaryName_.c_str(),
575 RootHelpText::title));
576 manPagesFile_ = compat::make_unique<TextWriter>(
577 outputRedirector_->openTextOutputFile("conf-man.py"));
578 manPagesFile_->writeLine("man_pages = [");
581 void HelpExportReStructuredText::exportModuleHelp(
582 const ICommandLineModule &module,
583 const std::string &tag,
584 const std::string &displayName)
586 TextOutputStreamPointer file
587 = outputRedirector_->openTextOutputFile("onlinehelp/" + tag + ".rst");
588 TextWriter writer(file);
589 writer.writeLine(formatString(".. _%s:", displayName.c_str()));
590 if (displayName == binaryName_ + " mdrun")
592 // Make an extra link target for the convenience of
593 // MPI-specific documentation
594 writer.writeLine(".. _mdrun_mpi:");
596 writer.ensureEmptyLine();
598 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_, binaryName_);
599 context.enterSubSection(displayName);
600 context.setModuleDisplayName(displayName);
601 module.writeHelp(context);
603 writer.ensureEmptyLine();
604 writer.writeLine(".. only:: man");
605 writer.writeLine();
606 writer.writeLine(" See also");
607 writer.writeLine(" --------");
608 writer.writeLine();
609 writer.writeLine(formatString(" :manpage:`%s(1)`", binaryName_.c_str()));
610 writer.writeLine();
611 writer.writeLine(" More information about |Gromacs| is available at <http://www.gromacs.org/>.");
612 file->close();
614 indexFile_->writeLine(formatString("* :doc:`%s </onlinehelp/%s>` - %s",
615 displayName.c_str(), tag.c_str(),
616 module.shortDescription()));
617 manPagesFile_->writeLine(
618 formatString(" ('onlinehelp/%s', '%s', \"%s\", '', 1),",
619 tag.c_str(), tag.c_str(), module.shortDescription()));
622 void HelpExportReStructuredText::finishModuleExport()
624 indexFile_->close();
625 indexFile_.reset();
626 // TODO: Generalize.
627 manPagesFile_->writeLine(
628 formatString(" ('onlinehelp/%s', '%s', '%s', '', 1)",
629 binaryName_.c_str(), binaryName_.c_str(),
630 RootHelpText::title));
631 manPagesFile_->writeLine("]");
632 manPagesFile_->close();
633 manPagesFile_.reset();
636 void HelpExportReStructuredText::startModuleGroupExport()
638 indexFile_ = compat::make_unique<TextWriter>(
639 outputRedirector_->openTextOutputFile("fragments/bytopic.rst"));
640 manPagesFile_ = compat::make_unique<TextWriter>(
641 outputRedirector_->openTextOutputFile("fragments/bytopic-man.rst"));
644 void HelpExportReStructuredText::exportModuleGroup(
645 const char *title,
646 const ModuleGroupContents &modules)
648 indexFile_->ensureEmptyLine();
649 indexFile_->writeLine(title);
650 indexFile_->writeLine(std::string(std::strlen(title), '^'));
651 manPagesFile_->ensureEmptyLine();
652 manPagesFile_->writeLine(title);
653 manPagesFile_->writeLine(std::string(std::strlen(title), '^'));
655 ModuleGroupContents::const_iterator module;
656 for (module = modules.begin(); module != modules.end(); ++module)
658 const std::string &tag(module->first);
659 std::string displayName(tag);
660 // TODO: This does not work if the binary name would contain a dash,
661 // but that is not currently the case.
662 const size_t dashPos = displayName.find('-');
663 GMX_RELEASE_ASSERT(dashPos != std::string::npos,
664 "There should always be at least one dash in the tag");
665 displayName[dashPos] = ' ';
666 indexFile_->writeLine(formatString(":doc:`%s </onlinehelp/%s>`\n %s",
667 displayName.c_str(), tag.c_str(),
668 module->second));
669 manPagesFile_->writeLine(formatString(":manpage:`%s(1)`\n %s",
670 tag.c_str(),
671 module->second));
675 void HelpExportReStructuredText::finishModuleGroupExport()
677 indexFile_->close();
678 indexFile_.reset();
679 manPagesFile_->close();
680 manPagesFile_.reset();
683 void HelpExportReStructuredText::exportTopic(const IHelpTopic &topic)
685 const std::string path("onlinehelp/" + std::string(topic.name()) + ".rst");
686 TextWriter writer(outputRedirector_->openTextOutputFile(path));
687 CommandLineHelpContext context(&writer, eHelpOutputFormat_Rst, &links_,
688 binaryName_);
689 HelpManager manager(topic, context.writerContext());
690 manager.writeCurrentTopic();
691 writer.close();
694 /********************************************************************
695 * HelpExportCompletion
698 /*! \internal \brief
699 * Implements export for command-line completion.
701 * \ingroup module_commandline
703 class HelpExportCompletion : public IHelpExport
705 public:
706 //! Initializes completion exporter.
707 explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
709 void startModuleExport() override;
710 void exportModuleHelp(
711 const ICommandLineModule &module,
712 const std::string &tag,
713 const std::string &displayName) override;
714 void finishModuleExport() override;
716 void startModuleGroupExport() override {}
717 void exportModuleGroup(const char * /*title*/,
718 const ModuleGroupContents & /*modules*/) override {}
719 void finishModuleGroupExport() override {}
721 void exportTopic(const IHelpTopic & /*topic*/) override {}
723 private:
724 ShellCompletionWriter bashWriter_;
725 std::vector<std::string> modules_;
728 HelpExportCompletion::HelpExportCompletion(
729 const CommandLineHelpModuleImpl &helpModule)
730 : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
734 void HelpExportCompletion::startModuleExport()
736 bashWriter_.startCompletions();
739 void HelpExportCompletion::exportModuleHelp(
740 const ICommandLineModule &module,
741 const std::string & /*tag*/,
742 const std::string & /*displayName*/)
744 modules_.emplace_back(module.name());
746 CommandLineHelpContext context(&bashWriter_);
747 // We use the display name to pass the name of the module to the
748 // completion writer.
749 context.setModuleDisplayName(module.name());
750 module.writeHelp(context);
754 void HelpExportCompletion::finishModuleExport()
756 CommandLineCommonOptionsHolder optionsHolder;
757 optionsHolder.initOptions();
758 bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
759 bashWriter_.finishCompletions();
762 } // namespace
764 /********************************************************************
765 * CommandLineHelpModuleImpl implementation
768 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
769 const IProgramContext &programContext,
770 const std::string &binaryName,
771 const CommandLineModuleMap &modules,
772 const CommandLineModuleGroupList &groups)
773 : rootTopic_(*this), programContext_(programContext),
774 binaryName_(binaryName), modules_(modules), groups_(groups),
775 context_(nullptr), moduleOverride_(nullptr), bHidden_(false),
776 outputRedirector_(&defaultFileOutputRedirector())
780 std::unique_ptr<IHelpExport>
781 CommandLineHelpModuleImpl::createExporter(const std::string &format,
782 IFileOutputRedirector *redirector)
784 if (format == "rst")
786 return std::unique_ptr<IHelpExport>(
787 new HelpExportReStructuredText(*this, redirector));
789 else if (format == "completion")
791 return std::unique_ptr<IHelpExport>(
792 new HelpExportCompletion(*this));
794 GMX_THROW(NotImplementedError("This help format is not implemented"));
797 void CommandLineHelpModuleImpl::exportHelp(IHelpExport *exporter)
799 // TODO: Would be nicer to have the file names supplied by the build system
800 // and/or export a list of files from here.
801 const char *const program = binaryName_.c_str();
803 exporter->startModuleExport();
804 CommandLineModuleMap::const_iterator module;
805 for (module = modules_.begin(); module != modules_.end(); ++module)
807 if (module->second->shortDescription() != nullptr)
809 const char *const moduleName = module->first.c_str();
810 std::string tag(formatString("%s-%s", program, moduleName));
811 std::string displayName(formatString("%s %s", program, moduleName));
812 exporter->exportModuleHelp(*module->second, tag, displayName);
815 exporter->finishModuleExport();
817 exporter->startModuleGroupExport();
818 CommandLineModuleGroupList::const_iterator group;
819 for (group = groups_.begin(); group != groups_.end(); ++group)
821 exporter->exportModuleGroup((*group)->title(), (*group)->modules());
823 exporter->finishModuleGroupExport();
825 rootTopic_.exportHelp(exporter);
828 namespace
831 /********************************************************************
832 * ModificationCheckingFileOutputStream
835 class ModificationCheckingFileOutputStream : public TextOutputStream
837 public:
838 ModificationCheckingFileOutputStream(
839 const char *path,
840 IFileOutputRedirector *redirector)
841 : path_(path), redirector_(redirector)
845 void write(const char *str) override { contents_.write(str); }
846 void close() override
848 const std::string &newContents = contents_.toString();
849 // TODO: Redirect these for unit tests.
850 if (File::exists(path_, File::returnFalseOnError))
852 const std::string originalContents_
853 = TextReader::readFileToString(path_);
854 if (originalContents_ == newContents)
856 return;
859 TextWriter writer(redirector_->openTextOutputFile(path_));
860 writer.writeString(newContents);
863 private:
864 std::string path_;
865 StringOutputStream contents_;
866 IFileOutputRedirector *redirector_;
869 /********************************************************************
870 * ModificationCheckingFileOutputRedirector
873 class ModificationCheckingFileOutputRedirector : public IFileOutputRedirector
875 public:
876 explicit ModificationCheckingFileOutputRedirector(
877 IFileOutputRedirector *redirector)
878 : redirector_(redirector)
882 TextOutputStream &standardOutput() override
884 return redirector_->standardOutput();
886 TextOutputStreamPointer openTextOutputFile(const char *filename) override
888 return TextOutputStreamPointer(
889 new ModificationCheckingFileOutputStream(filename, redirector_));
892 private:
893 IFileOutputRedirector *redirector_;
896 } // namespace
898 /********************************************************************
899 * CommandLineHelpModule
902 CommandLineHelpModule::CommandLineHelpModule(
903 const IProgramContext &programContext,
904 const std::string &binaryName,
905 const CommandLineModuleMap &modules,
906 const CommandLineModuleGroupList &groups)
907 : impl_(new Impl(programContext, binaryName, modules, groups))
911 CommandLineHelpModule::~CommandLineHelpModule()
915 HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
916 const ICommandLineModule &module) const
918 return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
921 void CommandLineHelpModule::addTopic(HelpTopicPointer topic, bool bExported)
923 impl_->rootTopic_.addTopic(std::move(topic), bExported);
926 void CommandLineHelpModule::setShowHidden(bool bHidden)
928 impl_->bHidden_ = bHidden;
931 void CommandLineHelpModule::setModuleOverride(
932 const ICommandLineModule &module)
934 impl_->moduleOverride_ = &module;
937 void CommandLineHelpModule::setOutputRedirector(
938 IFileOutputRedirector *output)
940 impl_->outputRedirector_ = output;
943 int CommandLineHelpModule::run(int argc, char *argv[])
945 // Add internal topics lazily here.
946 addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)), false);
948 const char *const exportFormats[] = { "rst", "completion" };
949 std::string exportFormat;
950 Options options;
951 options.addOption(StringOption("export").store(&exportFormat)
952 .enumValue(exportFormats));
953 CommandLineParser(&options).allowPositionalArguments(true).parse(&argc, argv);
954 if (!exportFormat.empty())
956 ModificationCheckingFileOutputRedirector redirector(impl_->outputRedirector_);
957 const std::unique_ptr<IHelpExport> exporter(
958 impl_->createExporter(exportFormat, &redirector));
959 impl_->exportHelp(exporter.get());
960 return 0;
963 TextOutputStream &outputFile = impl_->outputRedirector_->standardOutput();
964 TextWriter writer(&outputFile);
965 HelpLinks links(eHelpOutputFormat_Console);
966 initProgramLinks(&links, *impl_);
967 CommandLineHelpContext context(&writer, eHelpOutputFormat_Console, &links,
968 impl_->binaryName_);
969 context.setShowHidden(impl_->bHidden_);
970 if (impl_->moduleOverride_ != nullptr)
972 context.setModuleDisplayName(impl_->programContext_.displayName());
973 impl_->moduleOverride_->writeHelp(context);
974 return 0;
976 impl_->context_ = &context;
978 HelpManager helpManager(impl_->rootTopic_, context.writerContext());
981 for (int i = 1; i < argc; ++i)
983 helpManager.enterTopic(argv[i]);
986 catch (const InvalidInputError &ex)
988 fprintf(stderr, "%s\n", ex.what());
989 return 2;
991 helpManager.writeCurrentTopic();
992 return 0;
995 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext &context) const
997 const HelpWriterContext &writerContext = context.writerContext();
998 // TODO: Implement.
999 if (writerContext.outputFormat() != eHelpOutputFormat_Console)
1001 return;
1003 writerContext.writeTextBlock(
1004 "Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
1005 // TODO: More information.
1008 } // namespace gmx