2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010-2018, The GROMACS development team.
5 * Copyright (c) 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::CommandLineHelpWriter.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_commandline
45 #include "cmdlinehelpwriter.h"
52 #include "gromacs/commandline/cmdlinehelpcontext.h"
53 #include "gromacs/onlinehelp/helpwritercontext.h"
54 #include "gromacs/options/basicoptions.h"
55 #include "gromacs/options/filenameoption.h"
56 #include "gromacs/options/options.h"
57 #include "gromacs/options/optionsvisitor.h"
58 #include "gromacs/options/timeunitmanager.h"
59 #include "gromacs/utility/arrayref.h"
60 #include "gromacs/utility/exceptions.h"
61 #include "gromacs/utility/stringutil.h"
62 #include "gromacs/utility/textwriter.h"
64 #include "shellcompletions.h"
72 //! \addtogroup module_commandline
75 /********************************************************************
80 * Interface for output format specific formatting of options.
84 class IOptionsFormatter
87 virtual ~IOptionsFormatter() {}
89 //! Formats a single option option.
90 virtual void formatOption(const OptionInfo& option) = 0;
93 /********************************************************************
98 * Output format independent processing of options.
100 * Together with code in CommandLineHelpWriter::writeHelp(), this class
101 * implements the common logic for writing out the help.
102 * An object implementing the IOptionsFormatter must be provided to the
103 * constructor, and does the actual formatting that is specific to the output
106 class OptionsFilter : public OptionsVisitor
109 //! Specifies the type of output that formatSelected() produces.
112 eSelectInputFileOptions,
113 eSelectInputOutputFileOptions,
114 eSelectOutputFileOptions,
119 * Creates a new filtering object.
123 OptionsFilter() : formatter_(nullptr), filterType_(eSelectOtherOptions), bShowHidden_(false) {}
125 //! Sets whether hidden options will be shown.
126 void setShowHidden(bool bShowHidden) { bShowHidden_ = bShowHidden; }
128 //! Formats selected options using the formatter.
129 void formatSelected(FilterType type, IOptionsFormatter* formatter, const Options& options);
131 void visitSection(const OptionSectionInfo& section) override;
132 void visitOption(const OptionInfo& option) override;
135 IOptionsFormatter* formatter_;
136 FilterType filterType_;
139 GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
142 void OptionsFilter::formatSelected(FilterType type, IOptionsFormatter* formatter, const Options& options)
144 formatter_ = formatter;
146 visitSection(options.rootSection());
149 void OptionsFilter::visitSection(const OptionSectionInfo& section)
151 OptionsIterator iterator(section);
152 iterator.acceptSections(this);
153 iterator.acceptOptions(this);
156 void OptionsFilter::visitOption(const OptionInfo& option)
158 if (!bShowHidden_ && option.isHidden())
162 const FileNameOptionInfo* const fileOption = option.toType<FileNameOptionInfo>();
163 if (fileOption != nullptr && fileOption->isInputFile())
165 if (filterType_ == eSelectInputFileOptions)
167 formatter_->formatOption(option);
171 if (fileOption != nullptr && fileOption->isInputOutputFile())
173 if (filterType_ == eSelectInputOutputFileOptions)
175 formatter_->formatOption(option);
179 if (fileOption != nullptr && fileOption->isOutputFile())
181 if (filterType_ == eSelectOutputFileOptions)
183 formatter_->formatOption(option);
187 if (filterType_ == eSelectOtherOptions)
189 formatter_->formatOption(option);
194 /********************************************************************
195 * CommonFormatterData
198 class CommonFormatterData
201 explicit CommonFormatterData(const char* timeUnit) : timeUnit(timeUnit) {}
203 const char* timeUnit;
206 /********************************************************************
210 //! Formats option name and value.
211 void formatOptionNameAndValue(const OptionInfo& option, std::string* name, std::string* value)
213 *name = option.name();
214 *value = "<" + option.type() + ">";
215 if (option.isType<FileNameOptionInfo>())
217 // TODO: Make these work also for other option types.
218 if (option.maxValueCount() != 1)
222 if (option.minValueCount() == 0)
224 *value = "[" + *value + "]";
227 if (option.isType<BooleanOptionInfo>())
229 *name = "[no]" + *name;
230 // Old command-line parser doesn't accept any values for these.
231 // value = "[" + value + "]";
236 //! Formats the default option value as a string.
237 std::string defaultOptionValue(const OptionInfo& option)
239 return joinStrings(option.defaultValuesAsStrings(), " ");
242 //! Formats the flags for a file option as a string.
243 std::string fileOptionFlagsAsString(const FileNameOptionInfo& option, bool bAbbrev)
246 if (!option.isRequired())
248 type = bAbbrev ? "Opt." : "Optional";
250 if (option.isLibraryFile())
256 type.append(bAbbrev ? "Lib." : "Library");
261 //! Formats the description for an option as a string.
262 std::string descriptionWithOptionDetails(const CommonFormatterData& common, const OptionInfo& option)
264 std::string description(option.formatDescription());
266 const FloatOptionInfo* floatOption = option.toType<FloatOptionInfo>();
267 const DoubleOptionInfo* doubleOption = option.toType<DoubleOptionInfo>();
268 if ((floatOption != nullptr && floatOption->isTime())
269 || (doubleOption != nullptr && doubleOption->isTime()))
271 // TODO: It could be nicer to have this in basicoptions.cpp.
272 description = replaceAll(description, "%t", common.timeUnit);
278 /********************************************************************
279 * OptionsSynopsisFormatter
283 * Formatter implementation for synopsis.
285 class SynopsisFormatter : public IOptionsFormatter
288 //! Creates a helper object for formatting the synopsis.
289 explicit SynopsisFormatter(const HelpWriterContext& context) :
290 context_(context), bFormatted_(false), lineLength_(0), indent_(0), currentLength_(0)
294 //! Starts formatting the synopsis.
295 void start(const char* name);
296 //! Finishes formatting the synopsis.
299 void formatOption(const OptionInfo& option) override;
302 const HelpWriterContext& context_;
308 GMX_DISALLOW_COPY_AND_ASSIGN(SynopsisFormatter);
311 void SynopsisFormatter::start(const char* name)
313 currentLength_ = std::strlen(name) + 1;
314 indent_ = std::min(currentLength_, 13);
315 TextWriter& file = context_.outputFile();
316 switch (context_.outputFormat())
318 case eHelpOutputFormat_Console:
320 file.writeString(name);
322 case eHelpOutputFormat_Rst:
326 file.writeLine(".. parsed-literal::");
328 file.writeString(" ");
329 file.writeString(name);
332 GMX_THROW(NotImplementedError(
333 "Synopsis formatting not implemented for this output format"));
337 void SynopsisFormatter::finish()
339 context_.outputFile().ensureLineBreak();
342 void SynopsisFormatter::formatOption(const OptionInfo& option)
344 std::string name, value;
345 formatOptionNameAndValue(option, &name, &value);
346 int totalLength = name.length() + 4;
347 std::string fullOptionText = formatString(" [%s-%s", bFormatted_ ? ":strong:`" : "", name.c_str());
350 fullOptionText.append(bFormatted_ ? "` :emphasis:`" : " ");
351 fullOptionText.append(value);
352 totalLength += value.length() + 1;
354 fullOptionText.append(bFormatted_ ? "`]" : "]");
356 TextWriter& file = context_.outputFile();
357 currentLength_ += totalLength;
358 if (currentLength_ >= lineLength_)
360 file.writeString(formatString("\n%*c", indent_ - 1, ' '));
361 currentLength_ = indent_ - 1 + totalLength;
363 file.writeString(fullOptionText);
366 /********************************************************************
367 * OptionsListFormatter
371 * Formatter implementation for help export.
373 class OptionsListFormatter : public IOptionsFormatter
376 //! Creates a helper object for formatting options.
377 OptionsListFormatter(const HelpWriterContext& context, const CommonFormatterData& common, const char* title);
379 //! Initiates a new section in the options list.
380 void startSection(const char* header)
385 //! Finishes a section in the options list.
390 context_.writeOptionListEnd();
394 void formatOption(const OptionInfo& option) override;
397 void writeSectionStartIfNecessary()
399 if (title_ != nullptr)
401 context_.writeTitle(title_);
406 if (header_ != nullptr)
408 context_.paragraphBreak();
409 context_.writeTextBlock(header_);
410 context_.paragraphBreak();
412 context_.writeOptionListStart();
417 const HelpWriterContext& context_;
418 const CommonFormatterData& common_;
423 GMX_DISALLOW_COPY_AND_ASSIGN(OptionsListFormatter);
426 OptionsListFormatter::OptionsListFormatter(const HelpWriterContext& context,
427 const CommonFormatterData& common,
429 context_(context), common_(common), title_(title), header_(nullptr), bDidOutput_(false)
433 void OptionsListFormatter::formatOption(const OptionInfo& option)
435 writeSectionStartIfNecessary();
436 std::string name, value;
437 formatOptionNameAndValue(option, &name, &value);
438 std::string defaultValue(defaultOptionValue(option));
440 const FileNameOptionInfo* fileOption = option.toType<FileNameOptionInfo>();
441 if (fileOption != nullptr)
443 const bool bAbbrev = (context_.outputFormat() == eHelpOutputFormat_Console);
444 info = fileOptionFlagsAsString(*fileOption, bAbbrev);
446 std::string description(descriptionWithOptionDetails(common_, option));
447 context_.writeOptionItem("-" + name, value, defaultValue, info, description);
454 /********************************************************************
455 * CommandLineHelpWriter::Impl
459 * Private implementation class for CommandLineHelpWriter.
461 * \ingroup module_commandline
463 class CommandLineHelpWriter::Impl
466 //! Sets the Options object to use for generating help.
467 explicit Impl(const Options& options) : options_(options) {}
469 //! Format the list of known issues.
470 void formatBugs(const HelpWriterContext& context);
472 //! Options object to use for generating help.
473 const Options& options_;
475 std::string helpText_;
476 //! List of bugs/knows issues.
477 std::vector<std::string> bugs_;
480 void CommandLineHelpWriter::Impl::formatBugs(const HelpWriterContext& context)
486 context.writeTitle("Known Issues");
487 for (const auto& i : bugs_)
489 const char* const bug = i.c_str();
490 context.writeTextBlock(formatString("* %s", bug));
495 /********************************************************************
496 * CommandLineHelpWriter
499 CommandLineHelpWriter::CommandLineHelpWriter(const Options& options) : impl_(new Impl(options)) {}
501 CommandLineHelpWriter::~CommandLineHelpWriter() {}
503 CommandLineHelpWriter& CommandLineHelpWriter::setHelpText(const std::string& help)
505 impl_->helpText_ = help;
509 CommandLineHelpWriter& CommandLineHelpWriter::setHelpText(const ArrayRef<const char* const>& help)
511 impl_->helpText_ = joinStrings(help, "\n");
515 CommandLineHelpWriter& CommandLineHelpWriter::setKnownIssues(ArrayRef<const std::string> bugs)
517 impl_->bugs_ = std::vector<std::string>(bugs.begin(), bugs.end());
521 CommandLineHelpWriter& CommandLineHelpWriter::setKnownIssues(const ArrayRef<const char* const>& bugs)
523 impl_->bugs_ = std::vector<std::string>(bugs.begin(), bugs.end());
527 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext& context)
529 if (context.isCompletionExport())
531 context.shellCompletionWriter().writeModuleCompletions(context.moduleDisplayName(), impl_->options_);
534 const HelpWriterContext& writerContext = context.writerContext();
535 OptionsFilter filter;
536 filter.setShowHidden(context.showHidden());
539 writerContext.writeTitle("Synopsis");
540 SynopsisFormatter synopsisFormatter(writerContext);
541 synopsisFormatter.start(context.moduleDisplayName());
542 filter.formatSelected(OptionsFilter::eSelectInputFileOptions, &synopsisFormatter, impl_->options_);
543 filter.formatSelected(
544 OptionsFilter::eSelectInputOutputFileOptions, &synopsisFormatter, impl_->options_);
545 filter.formatSelected(OptionsFilter::eSelectOutputFileOptions, &synopsisFormatter, impl_->options_);
546 filter.formatSelected(OptionsFilter::eSelectOtherOptions, &synopsisFormatter, impl_->options_);
547 synopsisFormatter.finish();
550 if (!impl_->helpText_.empty())
552 writerContext.writeTitle("Description");
553 writerContext.writeTextBlock(impl_->helpText_);
555 CommonFormatterData common(TimeUnitManager().timeUnitAsString());
556 OptionsListFormatter formatter(writerContext, common, "Options");
557 formatter.startSection("Options to specify input files:");
558 filter.formatSelected(OptionsFilter::eSelectInputFileOptions, &formatter, impl_->options_);
559 formatter.finishSection();
560 formatter.startSection("Options to specify input/output files:");
561 filter.formatSelected(OptionsFilter::eSelectInputOutputFileOptions, &formatter, impl_->options_);
562 formatter.finishSection();
563 formatter.startSection("Options to specify output files:");
564 filter.formatSelected(OptionsFilter::eSelectOutputFileOptions, &formatter, impl_->options_);
565 formatter.finishSection();
566 formatter.startSection("Other options:");
567 filter.formatSelected(OptionsFilter::eSelectOtherOptions, &formatter, impl_->options_);
568 formatter.finishSection();
570 impl_->formatBugs(writerContext);