* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_commandline
*/
-#include "gromacs/commandline/cmdlinehelpmodule.h"
+#include "gmxpre.h"
+
+#include "cmdlinehelpmodule.h"
-#include <algorithm>
#include <string>
+#include <vector>
#include <boost/scoped_ptr.hpp>
-#include "gromacs/legacyheaders/copyrite.h"
-
#include "gromacs/commandline/cmdlinehelpcontext.h"
+#include "gromacs/commandline/cmdlinehelpwriter.h"
#include "gromacs/commandline/cmdlineparser.h"
#include "gromacs/onlinehelp/helpformat.h"
#include "gromacs/onlinehelp/helpmanager.h"
#include "gromacs/onlinehelp/helpwritercontext.h"
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/options.h"
+#include "gromacs/utility/baseversion.h"
#include "gromacs/utility/exceptions.h"
#include "gromacs/utility/file.h"
#include "gromacs/utility/gmxassert.h"
-#include "gromacs/utility/programinfo.h"
+#include "gromacs/utility/programcontext.h"
#include "gromacs/utility/stringutil.h"
+#include "shellcompletions.h"
+
namespace gmx
{
class CommandLineHelpModuleImpl
{
public:
- CommandLineHelpModuleImpl(const ProgramInfo &programInfo,
+ CommandLineHelpModuleImpl(const ProgramContextInterface &programContext,
+ const std::string &binaryName,
const CommandLineModuleMap &modules,
const CommandLineModuleGroupList &groups);
void exportHelp(HelpExportInterface *exporter) const;
boost::scoped_ptr<RootHelpTopic> rootTopic_;
- const ProgramInfo &programInfo_;
+ const ProgramContextInterface &programContext_;
+ std::string binaryName_;
const CommandLineModuleMap &modules_;
const CommandLineModuleGroupList &groups_;
const CommandLineModuleInterface *moduleOverride_;
bool bHidden_;
+ File *outputOverride_;
+
GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModuleImpl);
};
// The first two are not used.
const char RootHelpText::name[] = "";
const char RootHelpText::title[] = "";
-const char *const RootHelpText::text[] = {
- "Usage: [PROGRAM] <command> [<args>]",
-};
+const char *const RootHelpText::text[] = { "" };
/*! \brief
* Help topic that forms the root of the help tree for the help subcommand.
/*! \brief
* Creates a root help topic.
*
- * \param[in] modules List of modules for to use for module listings.
- *
* Does not throw.
*/
- explicit RootHelpTopic(const CommandLineModuleMap &modules)
- : modules_(modules)
+ explicit RootHelpTopic(const CommandLineHelpModuleImpl &helpModule)
+ : helpModule_(helpModule)
{
}
virtual void writeHelp(const HelpWriterContext &context) const;
private:
- void printModuleList(const HelpWriterContext &context) const;
-
- const CommandLineModuleMap &modules_;
+ const CommandLineHelpModuleImpl &helpModule_;
GMX_DISALLOW_COPY_AND_ASSIGN(RootHelpTopic);
};
GMX_THROW(NotImplementedError(
"Root help is not implemented for this output format"));
}
- writeBasicHelpTopic(context, *this, helpText());
- // TODO: If/when this list becomes long, it may be better to only print
- // "common" commands here, and have a separate topic (e.g.,
- // "help commands") that prints the full list.
- printModuleList(context);
- context.writeTextBlock(
- "For additional help on a command, use '[PROGRAM] help <command>'");
+ {
+ CommandLineCommonOptionsHolder optionsHolder;
+ CommandLineHelpContext cmdlineContext(*helpModule_.context_);
+ cmdlineContext.setModuleDisplayName(helpModule_.binaryName_);
+ optionsHolder.initOptions();
+ // TODO: Add <command> [<args>] into the synopsis.
+ CommandLineHelpWriter(*optionsHolder.options())
+ .writeHelp(cmdlineContext);
+ }
+ // TODO: Consider printing a list of "core" commands. Would require someone
+ // to determine such a set...
writeSubTopicList(context,
- "\nAdditional help is available on the following topics:");
+ "Additional help is available on the following topics:");
context.writeTextBlock(
- "To access the help, use '[PROGRAM] help <topic>'.");
+ "To access the help, use '[PROGRAM] help <topic>'.[BR]"
+ "For help on a command, use '[PROGRAM] help <command>'.");
}
-void RootHelpTopic::printModuleList(const HelpWriterContext &context) const
+/********************************************************************
+ * CommandsHelpTopic
+ */
+
+/*! \brief
+ * Help topic for listing the commands.
+ *
+ * \ingroup module_commandline
+ */
+class CommandsHelpTopic : public HelpTopicInterface
+{
+ public:
+ /*! \brief
+ * Creates a command list help topic.
+ *
+ * \param[in] helpModule Help module to get module information from.
+ *
+ * Does not throw.
+ */
+ explicit CommandsHelpTopic(const CommandLineHelpModuleImpl &helpModule)
+ : helpModule_(helpModule)
+ {
+ }
+
+ virtual const char *name() const { return "commands"; }
+ virtual const char *title() const { return "List of available commands"; }
+ virtual bool hasSubTopics() const { return false; }
+ virtual const HelpTopicInterface *findSubTopic(const char * /*name*/) const
+ {
+ return NULL;
+ }
+
+ virtual void writeHelp(const HelpWriterContext &context) const;
+
+ private:
+ const CommandLineHelpModuleImpl &helpModule_;
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(CommandsHelpTopic);
+};
+
+void CommandsHelpTopic::writeHelp(const HelpWriterContext &context) const
{
if (context.outputFormat() != eHelpOutputFormat_Console)
{
- // TODO: Implement once the situation with Redmine issue #969 is more
- // clear.
GMX_THROW(NotImplementedError(
"Module list is not implemented for this output format"));
}
int maxNameLength = 0;
- CommandLineModuleMap::const_iterator module;
- for (module = modules_.begin(); module != modules_.end(); ++module)
+ const CommandLineModuleMap &modules = helpModule_.modules_;
+ CommandLineModuleMap::const_iterator module;
+ for (module = modules.begin(); module != modules.end(); ++module)
{
int nameLength = static_cast<int>(module->first.length());
if (module->second->shortDescription() != NULL
maxNameLength = nameLength;
}
}
+ context.writeTextBlock(
+ "Usage: [PROGRAM] [<options>] <command> [<args>][PAR]"
+ "Available commands:");
File &file = context.outputFile();
TextTableFormatter formatter;
formatter.addColumn(NULL, maxNameLength + 1, false);
formatter.addColumn(NULL, 72 - maxNameLength, true);
formatter.setFirstColumnIndent(4);
- file.writeLine();
- file.writeLine("Available commands:");
- for (module = modules_.begin(); module != modules_.end(); ++module)
+ for (module = modules.begin(); module != modules.end(); ++module)
{
const char *name = module->first.c_str();
const char *description = module->second->shortDescription();
file.writeString(formatter.formatRow());
}
}
+ context.writeTextBlock(
+ "For help on a command, use '[PROGRAM] help <command>'.");
}
/********************************************************************
public:
//! Constructs a help topic for a specific module.
ModuleHelpTopic(const CommandLineModuleInterface &module,
- const CommandLineHelpModule &helpModule)
+ const CommandLineHelpModuleImpl &helpModule)
: module_(module), helpModule_(helpModule)
{
}
private:
const CommandLineModuleInterface &module_;
- const CommandLineHelpModule &helpModule_;
+ const CommandLineHelpModuleImpl &helpModule_;
GMX_DISALLOW_COPY_AND_ASSIGN(ModuleHelpTopic);
};
void ModuleHelpTopic::writeHelp(const HelpWriterContext & /*context*/) const
{
- CommandLineHelpContext context(helpModule_.context());
- const char *const program =
- helpModule_.programInfo().realBinaryName().c_str();
+ CommandLineHelpContext context(*helpModule_.context_);
+ const char *const program = helpModule_.binaryName_.c_str();
context.setModuleDisplayName(formatString("%s %s", program, module_.name()));
module_.writeHelp(context);
}
/*! \internal \brief
* Adds hyperlinks to modules within this binary.
*
- * \param[in,out] links Links are added here.
- * \param[in] modules Modules in the current binary.
+ * \param[in,out] links Links are added here.
+ * \param[in] helpModule Help module to get module information from.
* \throws std::bad_alloc if out of memory.
*
- * Initializes a HelpLinks object with links to modules defined in \p modules.
+ * Initializes a HelpLinks object with links to modules defined in
+ * \p helpModule.
*
* \ingroup module_commandline
*/
-void initProgramLinks(HelpLinks *links, const CommandLineModuleMap &modules)
+void initProgramLinks(HelpLinks *links, const CommandLineHelpModuleImpl &helpModule)
{
- const char *const program =
- ProgramInfo::getInstance().realBinaryName().c_str();
+ const char *const program = helpModule.binaryName_.c_str();
CommandLineModuleMap::const_iterator module;
- for (module = modules.begin(); module != modules.end(); ++module)
+ for (module = helpModule.modules_.begin();
+ module != helpModule.modules_.end();
+ ++module)
{
if (module->second->shortDescription() != NULL)
{
{
public:
//! Initializes man page exporter.
- explicit HelpExportMan(const CommandLineModuleMap &modules)
+ explicit HelpExportMan(const CommandLineHelpModuleImpl &helpModule)
: links_(eHelpOutputFormat_Man)
{
- initProgramLinks(&links_, modules);
+ initProgramLinks(&links_, helpModule);
}
virtual void startModuleExport() {}
// TODO: It would be nice to remove the VERSION prefix from the version
// string to make it shorter.
file.writeLine(formatString(".TH %s 1 \"\" \"%s\" \"GROMACS Manual\"\n",
- tag.c_str(),
- GromacsVersion()));
+ tag.c_str(), gmx_version()));
file.writeLine(".SH NAME");
file.writeLine(formatString("%s - %s", tag.c_str(),
module.shortDescription()));
"gromacs.7.in must contain a @PROGMANPAGES@ line");
std::string header = man7Template.substr(0, index);
man7Footer_ = man7Template.substr(index + std::strlen(programListPlaceholder));
- header = replaceAll(header, "@VERSION@", GromacsVersion());
+ header = replaceAll(header, "@VERSION@", gmx_version());
man7File_.reset(new File("man7/gromacs.7", "w"));
man7File_->writeLine(header);
}
{
public:
//! Initializes HTML exporter.
- explicit HelpExportHtml(const CommandLineModuleMap &modules);
+ explicit HelpExportHtml(const CommandLineHelpModuleImpl &helpModule);
virtual void startModuleExport();
virtual void exportModuleHelp(
std::string footer_;
};
-HelpExportHtml::HelpExportHtml(const CommandLineModuleMap &modules)
+HelpExportHtml::HelpExportHtml(const CommandLineHelpModuleImpl &helpModule)
: links_(eHelpOutputFormat_Html)
{
- initProgramLinks(&links_, modules);
File linksFile("links.dat", "r");
std::string line;
while (linksFile.readLine(&line))
links_.addLink(line, "../online/" + line, line);
}
linksFile.close();
+ initProgramLinks(&links_, helpModule);
setupHeaderAndFooter();
}
void HelpExportHtml::setupHeaderAndFooter()
{
header_ = gmx::File::readToString("header.html.in");
- header_ = replaceAll(header_, "@VERSION@", GromacsVersion());
+ header_ = replaceAll(header_, "@VERSION@", gmx_version());
gmx::File::writeFileFromString("header.html", header_);
header_ = replaceAll(header_, "@ROOTPATH@", "../");
footer_ = gmx::File::readToString("footer.html");
{
const std::string &tag(module->first);
std::string displayName(tag);
- std::replace(displayName.begin(), displayName.end(), '-', ' ');
+ // TODO: This does not work if the binary name would contain a dash,
+ // but that is not currently the case.
+ size_t dashPos = displayName.find('-');
+ GMX_RELEASE_ASSERT(dashPos != std::string::npos,
+ "There should always be at least one dash in the tag");
+ displayName[dashPos] = ' ';
indexFile_->writeLine(formatString("<a href=\"%s.html\">%s</a> - %s<br>",
tag.c_str(), displayName.c_str(),
module->second));
file->writeLine(footer_);
}
+/********************************************************************
+ * HelpExportCompletion
+ */
+
+/*! \internal \brief
+ * Implements export for command-line completion.
+ *
+ * \ingroup module_commandline
+ */
+class HelpExportCompletion : public HelpExportInterface
+{
+ public:
+ //! Initializes completion exporter.
+ explicit HelpExportCompletion(const CommandLineHelpModuleImpl &helpModule);
+
+ virtual void startModuleExport();
+ virtual void exportModuleHelp(
+ const CommandLineModuleInterface &module,
+ const std::string &tag,
+ const std::string &displayName);
+ virtual void finishModuleExport();
+
+ virtual void startModuleGroupExport() {}
+ virtual void exportModuleGroup(const char * /*title*/,
+ const ModuleGroupContents & /*modules*/) {}
+ virtual void finishModuleGroupExport() {}
+
+ private:
+ ShellCompletionWriter bashWriter_;
+ std::vector<std::string> modules_;
+};
+
+HelpExportCompletion::HelpExportCompletion(
+ const CommandLineHelpModuleImpl &helpModule)
+ : bashWriter_(helpModule.binaryName_, eShellCompletionFormat_Bash)
+{
+}
+
+void HelpExportCompletion::startModuleExport()
+{
+ bashWriter_.startCompletions();
+}
+
+void HelpExportCompletion::exportModuleHelp(
+ const CommandLineModuleInterface &module,
+ const std::string & /*tag*/,
+ const std::string & /*displayName*/)
+{
+ modules_.push_back(module.name());
+ {
+ CommandLineHelpContext context(&bashWriter_);
+ // We use the display name to pass the name of the module to the
+ // completion writer.
+ context.setModuleDisplayName(module.name());
+ module.writeHelp(context);
+ }
+}
+
+void HelpExportCompletion::finishModuleExport()
+{
+ CommandLineCommonOptionsHolder optionsHolder;
+ optionsHolder.initOptions();
+ bashWriter_.writeWrapperCompletions(modules_, *optionsHolder.options());
+ bashWriter_.finishCompletions();
+}
+
} // namespace
/********************************************************************
* CommandLineHelpModuleImpl implementation
*/
CommandLineHelpModuleImpl::CommandLineHelpModuleImpl(
- const ProgramInfo &programInfo,
+ const ProgramContextInterface &programContext,
+ const std::string &binaryName,
const CommandLineModuleMap &modules,
const CommandLineModuleGroupList &groups)
- : rootTopic_(new RootHelpTopic(modules)), programInfo_(programInfo),
- modules_(modules), groups_(groups),
- context_(NULL), moduleOverride_(NULL), bHidden_(false)
+ : rootTopic_(new RootHelpTopic(*this)), programContext_(programContext),
+ binaryName_(binaryName), modules_(modules), groups_(groups),
+ context_(NULL), moduleOverride_(NULL), bHidden_(false),
+ outputOverride_(NULL)
{
}
{
// TODO: Would be nicer to have the file names supplied by the build system
// and/or export a list of files from here.
- const char *const program = programInfo_.realBinaryName().c_str();
+ const char *const program = binaryName_.c_str();
exporter->startModuleExport();
CommandLineModuleMap::const_iterator module;
{
const char *const moduleName = module->first.c_str();
std::string tag(formatString("%s-%s", program, moduleName));
- std::string displayName(tag);
- std::replace(displayName.begin(), displayName.end(), '-', ' ');
+ std::string displayName(formatString("%s %s", program, moduleName));
exporter->exportModuleHelp(*module->second, tag, displayName);
}
}
*/
CommandLineHelpModule::CommandLineHelpModule(
- const ProgramInfo &programInfo,
+ const ProgramContextInterface &programContext,
+ const std::string &binaryName,
const CommandLineModuleMap &modules,
const CommandLineModuleGroupList &groups)
- : impl_(new Impl(programInfo, modules, groups))
+ : impl_(new Impl(programContext, binaryName, modules, groups))
{
}
HelpTopicPointer CommandLineHelpModule::createModuleHelpTopic(
const CommandLineModuleInterface &module) const
{
- return HelpTopicPointer(new ModuleHelpTopic(module, *this));
+ return HelpTopicPointer(new ModuleHelpTopic(module, *impl_));
}
void CommandLineHelpModule::addTopic(HelpTopicPointer topic)
impl_->moduleOverride_ = &module;
}
-const CommandLineHelpContext &CommandLineHelpModule::context() const
-{
- return *impl_->context_;
-}
-
-const ProgramInfo &CommandLineHelpModule::programInfo() const
+void CommandLineHelpModule::setOutputRedirect(File *output)
{
- return impl_->programInfo_;
+ impl_->outputOverride_ = output;
}
int CommandLineHelpModule::run(int argc, char *argv[])
{
+ // Add internal topics lazily here.
+ addTopic(HelpTopicPointer(new CommandsHelpTopic(*impl_)));
+
const char *const exportFormats[] = { "man", "html", "completion" };
std::string exportFormat;
Options options(NULL, NULL);
boost::scoped_ptr<HelpExportInterface> exporter;
if (exportFormat == "man")
{
- exporter.reset(new HelpExportMan(impl_->modules_));
+ exporter.reset(new HelpExportMan(*impl_));
}
else if (exportFormat == "html")
{
- exporter.reset(new HelpExportHtml(impl_->modules_));
+ exporter.reset(new HelpExportHtml(*impl_));
+ }
+ else if (exportFormat == "completion")
+ {
+ exporter.reset(new HelpExportCompletion(*impl_));
}
else
{
return 0;
}
+ File *outputFile = &File::standardOutput();
+ if (impl_->outputOverride_ != NULL)
+ {
+ outputFile = impl_->outputOverride_;
+ }
HelpLinks links(eHelpOutputFormat_Console);
- initProgramLinks(&links, impl_->modules_);
+ initProgramLinks(&links, *impl_);
boost::scoped_ptr<CommandLineHelpContext> context(
- new CommandLineHelpContext(&File::standardOutput(),
+ new CommandLineHelpContext(outputFile,
eHelpOutputFormat_Console, &links));
context->setShowHidden(impl_->bHidden_);
if (impl_->moduleOverride_ != NULL)
{
- context->setModuleDisplayName(impl_->programInfo_.displayName());
+ context->setModuleDisplayName(impl_->programContext_.displayName());
impl_->moduleOverride_->writeHelp(*context);
return 0;
}