SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpwriter.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
9  *
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.
14  *
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.
19  *
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.
24  *
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.
32  *
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.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements gmx::CommandLineHelpWriter.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_commandline
42  */
43 #include "gmxpre.h"
44
45 #include "cmdlinehelpwriter.h"
46
47 #include <cstring>
48
49 #include <algorithm>
50 #include <string>
51
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"
63
64 #include "shellcompletions.h"
65
66 namespace gmx
67 {
68
69 namespace
70 {
71
72 //! \addtogroup module_commandline
73 //! \{
74
75 /********************************************************************
76  * IOptionsFormatter
77  */
78
79 /*! \brief
80  * Interface for output format specific formatting of options.
81  *
82  * \see OptionsFilter
83  */
84 class IOptionsFormatter
85 {
86 public:
87     virtual ~IOptionsFormatter() {}
88
89     //! Formats a single option option.
90     virtual void formatOption(const OptionInfo& option) = 0;
91 };
92
93 /********************************************************************
94  * OptionsFilter
95  */
96
97 /*! \brief
98  * Output format independent processing of options.
99  *
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
104  * format.
105  */
106 class OptionsFilter : public OptionsVisitor
107 {
108 public:
109     //! Specifies the type of output that formatSelected() produces.
110     enum FilterType
111     {
112         eSelectInputFileOptions,
113         eSelectInputOutputFileOptions,
114         eSelectOutputFileOptions,
115         eSelectOtherOptions
116     };
117
118     /*! \brief
119      * Creates a new filtering object.
120      *
121      * Does not throw.
122      */
123     OptionsFilter() : formatter_(nullptr), filterType_(eSelectOtherOptions), bShowHidden_(false) {}
124
125     //! Sets whether hidden options will be shown.
126     void setShowHidden(bool bShowHidden) { bShowHidden_ = bShowHidden; }
127
128     //! Formats selected options using the formatter.
129     void formatSelected(FilterType type, IOptionsFormatter* formatter, const Options& options);
130
131     void visitSection(const OptionSectionInfo& section) override;
132     void visitOption(const OptionInfo& option) override;
133
134 private:
135     IOptionsFormatter* formatter_;
136     FilterType         filterType_;
137     bool               bShowHidden_;
138
139     GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
140 };
141
142 void OptionsFilter::formatSelected(FilterType type, IOptionsFormatter* formatter, const Options& options)
143 {
144     formatter_  = formatter;
145     filterType_ = type;
146     visitSection(options.rootSection());
147 }
148
149 void OptionsFilter::visitSection(const OptionSectionInfo& section)
150 {
151     OptionsIterator iterator(section);
152     iterator.acceptSections(this);
153     iterator.acceptOptions(this);
154 }
155
156 void OptionsFilter::visitOption(const OptionInfo& option)
157 {
158     if (!bShowHidden_ && option.isHidden())
159     {
160         return;
161     }
162     const FileNameOptionInfo* const fileOption = option.toType<FileNameOptionInfo>();
163     if (fileOption != nullptr && fileOption->isInputFile())
164     {
165         if (filterType_ == eSelectInputFileOptions)
166         {
167             formatter_->formatOption(option);
168         }
169         return;
170     }
171     if (fileOption != nullptr && fileOption->isInputOutputFile())
172     {
173         if (filterType_ == eSelectInputOutputFileOptions)
174         {
175             formatter_->formatOption(option);
176         }
177         return;
178     }
179     if (fileOption != nullptr && fileOption->isOutputFile())
180     {
181         if (filterType_ == eSelectOutputFileOptions)
182         {
183             formatter_->formatOption(option);
184         }
185         return;
186     }
187     if (filterType_ == eSelectOtherOptions)
188     {
189         formatter_->formatOption(option);
190         return;
191     }
192 }
193
194 /********************************************************************
195  * CommonFormatterData
196  */
197
198 class CommonFormatterData
199 {
200 public:
201     explicit CommonFormatterData(const char* timeUnit) : timeUnit(timeUnit) {}
202
203     const char* timeUnit;
204 };
205
206 /********************************************************************
207  * Helper functions
208  */
209
210 //! Formats option name and value.
211 void formatOptionNameAndValue(const OptionInfo& option, std::string* name, std::string* value)
212 {
213     *name  = option.name();
214     *value = "<" + option.type() + ">";
215     if (option.isType<FileNameOptionInfo>())
216     {
217         // TODO: Make these work also for other option types.
218         if (option.maxValueCount() != 1)
219         {
220             *value += " [...]";
221         }
222         if (option.minValueCount() == 0)
223         {
224             *value = "[" + *value + "]";
225         }
226     }
227     if (option.isType<BooleanOptionInfo>())
228     {
229         *name = "[no]" + *name;
230         // Old command-line parser doesn't accept any values for these.
231         // value = "[" + value + "]";
232         value->clear();
233     }
234 }
235
236 //! Formats the default option value as a string.
237 std::string defaultOptionValue(const OptionInfo& option)
238 {
239     return joinStrings(option.defaultValuesAsStrings(), " ");
240 }
241
242 //! Formats the flags for a file option as a string.
243 std::string fileOptionFlagsAsString(const FileNameOptionInfo& option, bool bAbbrev)
244 {
245     std::string type;
246     if (!option.isRequired())
247     {
248         type = bAbbrev ? "Opt." : "Optional";
249     }
250     if (option.isLibraryFile())
251     {
252         if (!type.empty())
253         {
254             type.append(", ");
255         }
256         type.append(bAbbrev ? "Lib." : "Library");
257     }
258     return type;
259 }
260
261 //! Formats the description for an option as a string.
262 std::string descriptionWithOptionDetails(const CommonFormatterData& common, const OptionInfo& option)
263 {
264     std::string description(option.formatDescription());
265
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()))
270     {
271         // TODO: It could be nicer to have this in basicoptions.cpp.
272         description = replaceAll(description, "%t", common.timeUnit);
273     }
274
275     return description;
276 }
277
278 /********************************************************************
279  * OptionsSynopsisFormatter
280  */
281
282 /*! \brief
283  * Formatter implementation for synopsis.
284  */
285 class SynopsisFormatter : public IOptionsFormatter
286 {
287 public:
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)
291     {
292     }
293
294     //! Starts formatting the synopsis.
295     void start(const char* name);
296     //! Finishes formatting the synopsis.
297     void finish();
298
299     void formatOption(const OptionInfo& option) override;
300
301 private:
302     const HelpWriterContext& context_;
303     bool                     bFormatted_;
304     int                      lineLength_;
305     int                      indent_;
306     int                      currentLength_;
307
308     GMX_DISALLOW_COPY_AND_ASSIGN(SynopsisFormatter);
309 };
310
311 void SynopsisFormatter::start(const char* name)
312 {
313     currentLength_   = std::strlen(name) + 1;
314     indent_          = std::min(currentLength_, 13);
315     TextWriter& file = context_.outputFile();
316     switch (context_.outputFormat())
317     {
318         case eHelpOutputFormat_Console:
319             lineLength_ = 78;
320             file.writeString(name);
321             break;
322         case eHelpOutputFormat_Rst:
323             bFormatted_ = true;
324             lineLength_ = 74;
325             indent_ += 4;
326             file.writeLine(".. parsed-literal::");
327             file.writeLine();
328             file.writeString("    ");
329             file.writeString(name);
330             break;
331         default:
332             GMX_THROW(NotImplementedError(
333                     "Synopsis formatting not implemented for this output format"));
334     }
335 }
336
337 void SynopsisFormatter::finish()
338 {
339     context_.outputFile().ensureLineBreak();
340 }
341
342 void SynopsisFormatter::formatOption(const OptionInfo& option)
343 {
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());
348     if (!value.empty())
349     {
350         fullOptionText.append(bFormatted_ ? "` :emphasis:`" : " ");
351         fullOptionText.append(value);
352         totalLength += value.length() + 1;
353     }
354     fullOptionText.append(bFormatted_ ? "`]" : "]");
355
356     TextWriter& file = context_.outputFile();
357     currentLength_ += totalLength;
358     if (currentLength_ >= lineLength_)
359     {
360         file.writeString(formatString("\n%*c", indent_ - 1, ' '));
361         currentLength_ = indent_ - 1 + totalLength;
362     }
363     file.writeString(fullOptionText);
364 }
365
366 /********************************************************************
367  * OptionsListFormatter
368  */
369
370 /*! \brief
371  * Formatter implementation for help export.
372  */
373 class OptionsListFormatter : public IOptionsFormatter
374 {
375 public:
376     //! Creates a helper object for formatting options.
377     OptionsListFormatter(const HelpWriterContext& context, const CommonFormatterData& common, const char* title);
378
379     //! Initiates a new section in the options list.
380     void startSection(const char* header)
381     {
382         header_     = header;
383         bDidOutput_ = false;
384     }
385     //! Finishes a section in the options list.
386     void finishSection()
387     {
388         if (bDidOutput_)
389         {
390             context_.writeOptionListEnd();
391         }
392     }
393
394     void formatOption(const OptionInfo& option) override;
395
396 private:
397     void writeSectionStartIfNecessary()
398     {
399         if (title_ != nullptr)
400         {
401             context_.writeTitle(title_);
402             title_ = nullptr;
403         }
404         if (!bDidOutput_)
405         {
406             if (header_ != nullptr)
407             {
408                 context_.paragraphBreak();
409                 context_.writeTextBlock(header_);
410                 context_.paragraphBreak();
411             }
412             context_.writeOptionListStart();
413         }
414         bDidOutput_ = true;
415     }
416
417     const HelpWriterContext&   context_;
418     const CommonFormatterData& common_;
419     const char*                title_;
420     const char*                header_;
421     bool                       bDidOutput_;
422
423     GMX_DISALLOW_COPY_AND_ASSIGN(OptionsListFormatter);
424 };
425
426 OptionsListFormatter::OptionsListFormatter(const HelpWriterContext&   context,
427                                            const CommonFormatterData& common,
428                                            const char*                title) :
429     context_(context), common_(common), title_(title), header_(nullptr), bDidOutput_(false)
430 {
431 }
432
433 void OptionsListFormatter::formatOption(const OptionInfo& option)
434 {
435     writeSectionStartIfNecessary();
436     std::string name, value;
437     formatOptionNameAndValue(option, &name, &value);
438     std::string               defaultValue(defaultOptionValue(option));
439     std::string               info;
440     const FileNameOptionInfo* fileOption = option.toType<FileNameOptionInfo>();
441     if (fileOption != nullptr)
442     {
443         const bool bAbbrev = (context_.outputFormat() == eHelpOutputFormat_Console);
444         info               = fileOptionFlagsAsString(*fileOption, bAbbrev);
445     }
446     std::string description(descriptionWithOptionDetails(common_, option));
447     context_.writeOptionItem("-" + name, value, defaultValue, info, description);
448 }
449
450 //! \}
451
452 } // namespace
453
454 /********************************************************************
455  * CommandLineHelpWriter::Impl
456  */
457
458 /*! \internal \brief
459  * Private implementation class for CommandLineHelpWriter.
460  *
461  * \ingroup module_commandline
462  */
463 class CommandLineHelpWriter::Impl
464 {
465 public:
466     //! Sets the Options object to use for generating help.
467     explicit Impl(const Options& options) : options_(options) {}
468
469     //! Format the list of known issues.
470     void formatBugs(const HelpWriterContext& context);
471
472     //! Options object to use for generating help.
473     const Options& options_;
474     //! Help text.
475     std::string helpText_;
476     //! List of bugs/knows issues.
477     std::vector<std::string> bugs_;
478 };
479
480 void CommandLineHelpWriter::Impl::formatBugs(const HelpWriterContext& context)
481 {
482     if (bugs_.empty())
483     {
484         return;
485     }
486     context.writeTitle("Known Issues");
487     for (const auto& i : bugs_)
488     {
489         const char* const bug = i.c_str();
490         context.writeTextBlock(formatString("* %s", bug));
491     }
492 }
493
494
495 /********************************************************************
496  * CommandLineHelpWriter
497  */
498
499 CommandLineHelpWriter::CommandLineHelpWriter(const Options& options) : impl_(new Impl(options)) {}
500
501 CommandLineHelpWriter::~CommandLineHelpWriter() {}
502
503 CommandLineHelpWriter& CommandLineHelpWriter::setHelpText(const std::string& help)
504 {
505     impl_->helpText_ = help;
506     return *this;
507 }
508
509 CommandLineHelpWriter& CommandLineHelpWriter::setHelpText(const ArrayRef<const char* const>& help)
510 {
511     impl_->helpText_ = joinStrings(help, "\n");
512     return *this;
513 }
514
515 CommandLineHelpWriter& CommandLineHelpWriter::setKnownIssues(ArrayRef<const std::string> bugs)
516 {
517     impl_->bugs_ = std::vector<std::string>(bugs.begin(), bugs.end());
518     return *this;
519 }
520
521 CommandLineHelpWriter& CommandLineHelpWriter::setKnownIssues(const ArrayRef<const char* const>& bugs)
522 {
523     impl_->bugs_ = std::vector<std::string>(bugs.begin(), bugs.end());
524     return *this;
525 }
526
527 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext& context)
528 {
529     if (context.isCompletionExport())
530     {
531         context.shellCompletionWriter().writeModuleCompletions(context.moduleDisplayName(), impl_->options_);
532         return;
533     }
534     const HelpWriterContext& writerContext = context.writerContext();
535     OptionsFilter            filter;
536     filter.setShowHidden(context.showHidden());
537
538     {
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();
548     }
549
550     if (!impl_->helpText_.empty())
551     {
552         writerContext.writeTitle("Description");
553         writerContext.writeTextBlock(impl_->helpText_);
554     }
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();
569
570     impl_->formatBugs(writerContext);
571 }
572
573 } // namespace gmx