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.
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()), line
);
532 links_
.addLink("[REF]" + line
+ "[ref]", formatString(":ref:`%s`", line
.c_str()), line
);
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
));
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");
573 writer
.writeLine(" See also");
574 writer
.writeLine(" --------");
576 writer
.writeLine(formatString(" :manpage:`%s(1)`", binaryName_
.c_str()));
579 " More information about |Gromacs| is available at <http://www.gromacs.org/>.");
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()
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()
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();
652 /********************************************************************
653 * HelpExportCompletion
657 * Implements export for command-line completion.
659 * \ingroup module_commandline
661 class HelpExportCompletion
: public IHelpExport
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
{}
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();
720 /********************************************************************
721 * CommandLineHelpModuleImpl implementation
724 CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(const IProgramContext
& programContext
,
725 const std::string
& binaryName
,
726 const CommandLineModuleMap
& modules
,
727 const CommandLineModuleGroupList
& groups
) :
729 programContext_(programContext
),
730 binaryName_(binaryName
),
734 moduleOverride_(nullptr),
736 outputRedirector_(&defaultFileOutputRedirector())
740 std::unique_ptr
<IHelpExport
> CommandLineHelpModuleImpl::createExporter(const std::string
& format
,
741 IFileOutputRedirector
* redirector
)
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
);
788 /********************************************************************
789 * ModificationCheckingFileOutputStream
792 class ModificationCheckingFileOutputStream
: public TextOutputStream
795 ModificationCheckingFileOutputStream(const char* path
, IFileOutputRedirector
* redirector
) :
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
)
814 TextWriter
writer(redirector_
->openTextOutputFile(path_
));
815 writer
.writeString(newContents
);
820 StringOutputStream contents_
;
821 IFileOutputRedirector
* redirector_
;
824 /********************************************************************
825 * ModificationCheckingFileOutputRedirector
828 class ModificationCheckingFileOutputRedirector
: public IFileOutputRedirector
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_
));
843 IFileOutputRedirector
* redirector_
;
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
;
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());
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
);
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());
932 helpManager
.writeCurrentTopic();
936 void CommandLineHelpModule::writeHelp(const CommandLineHelpContext
& context
) const
938 const HelpWriterContext
& writerContext
= context
.writerContext();
940 if (writerContext
.outputFormat() != eHelpOutputFormat_Console
)
944 writerContext
.writeTextBlock("Usage: [PROGRAM] help [<command>|<topic> [<subtopic> [...]]]");
945 // TODO: More information.