Replace some [BR] in help texts with rst literals
[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,2011,2012,2013,2014,2015, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements gmx::CommandLineHelpWriter.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_commandline
41  */
42 #include "gmxpre.h"
43
44 #include "cmdlinehelpwriter.h"
45
46 #include <cstring>
47
48 #include <algorithm>
49 #include <string>
50
51 #include <boost/scoped_ptr.hpp>
52
53 #include "gromacs/commandline/cmdlinehelpcontext.h"
54 #include "gromacs/onlinehelp/helpformat.h"
55 #include "gromacs/onlinehelp/helpwritercontext.h"
56 #include "gromacs/options/basicoptions.h"
57 #include "gromacs/options/filenameoption.h"
58 #include "gromacs/options/options.h"
59 #include "gromacs/options/optionsvisitor.h"
60 #include "gromacs/options/timeunitmanager.h"
61 #include "gromacs/utility/arrayref.h"
62 #include "gromacs/utility/exceptions.h"
63 #include "gromacs/utility/file.h"
64 #include "gromacs/utility/stringutil.h"
65
66 #include "shellcompletions.h"
67
68 namespace gmx
69 {
70
71 namespace
72 {
73
74 //! \addtogroup module_commandline
75 //! \{
76
77 /********************************************************************
78  * DescriptionsFormatter
79  */
80
81 class DescriptionsFormatter : public OptionsVisitor
82 {
83     public:
84         /*! \brief
85          * Creates a new description formatter.
86          *
87          * \param[in] context   Help context to use to write the help.
88          */
89         explicit DescriptionsFormatter(const HelpWriterContext &context)
90             : context_(context), title_(NULL), bDidOutput_(false)
91         {
92         }
93
94         //! Formats all section descriptions from a given options.
95         void format(const Options &options, const char *title)
96         {
97             title_      = title;
98             bDidOutput_ = false;
99             visitSubSection(options);
100             if (bDidOutput_)
101             {
102                 context_.outputFile().writeLine();
103             }
104         }
105
106         //! Formats the description for a single subsection and handles recursion.
107         virtual void visitSubSection(const Options &section);
108         // This method is not used and never called.
109         virtual void visitOption(const OptionInfo & /*option*/) {}
110
111     private:
112         const HelpWriterContext &context_;
113         const char              *title_;
114         bool                     bDidOutput_;
115
116         GMX_DISALLOW_COPY_AND_ASSIGN(DescriptionsFormatter);
117 };
118
119 void DescriptionsFormatter::visitSubSection(const Options &section)
120 {
121     if (!section.description().empty())
122     {
123         if (bDidOutput_)
124         {
125             context_.outputFile().writeLine();
126         }
127         else if (title_ != NULL)
128         {
129             context_.writeTitle(title_);
130         }
131         // TODO: Print title for the section?
132         context_.writeTextBlock(section.description());
133         bDidOutput_ = true;
134     }
135
136     OptionsIterator iterator(section);
137     iterator.acceptSubSections(this);
138 }
139
140 /********************************************************************
141  * OptionsFormatterInterface
142  */
143
144 /*! \brief
145  * Interface for output format specific formatting of options.
146  *
147  * \see OptionsFilter
148  */
149 class OptionsFormatterInterface
150 {
151     public:
152         virtual ~OptionsFormatterInterface() {}
153
154         //! Formats a single option option.
155         virtual void formatOption(const OptionInfo &option) = 0;
156 };
157
158 /********************************************************************
159  * OptionsFilter
160  */
161
162 /*! \brief
163  * Output format independent processing of options.
164  *
165  * Together with code in CommandLineHelpWriter::writeHelp(), this class
166  * implements the common logic for writing out the help.
167  * An object implementing the OptionsFormatterInterface must be provided to the
168  * constructor, and does the actual formatting that is specific to the output
169  * format.
170  */
171 class OptionsFilter : public OptionsVisitor
172 {
173     public:
174         //! Specifies the type of output that formatSelected() produces.
175         enum FilterType
176         {
177             eSelectFileOptions,
178             eSelectOtherOptions
179         };
180
181         /*! \brief
182          * Creates a new filtering object.
183          *
184          * Does not throw.
185          */
186         OptionsFilter()
187             : formatter_(NULL), filterType_(eSelectOtherOptions),
188               bShowHidden_(false)
189         {
190         }
191
192         //! Sets whether hidden options will be shown.
193         void setShowHidden(bool bShowHidden)
194         {
195             bShowHidden_ = bShowHidden;
196         }
197
198         //! Formats selected options using the formatter.
199         void formatSelected(FilterType                 type,
200                             OptionsFormatterInterface *formatter,
201                             const Options             &options);
202
203         virtual void visitSubSection(const Options &section);
204         virtual void visitOption(const OptionInfo &option);
205
206     private:
207         OptionsFormatterInterface      *formatter_;
208         FilterType                      filterType_;
209         bool                            bShowHidden_;
210
211         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsFilter);
212 };
213
214 void OptionsFilter::formatSelected(FilterType                 type,
215                                    OptionsFormatterInterface *formatter,
216                                    const Options             &options)
217 {
218     formatter_  = formatter;
219     filterType_ = type;
220     visitSubSection(options);
221 }
222
223 void OptionsFilter::visitSubSection(const Options &section)
224 {
225     OptionsIterator iterator(section);
226     iterator.acceptSubSections(this);
227     iterator.acceptOptions(this);
228 }
229
230 void OptionsFilter::visitOption(const OptionInfo &option)
231 {
232     if (!bShowHidden_ && option.isHidden())
233     {
234         return;
235     }
236     if (option.isType<FileNameOptionInfo>())
237     {
238         if (filterType_ == eSelectFileOptions)
239         {
240             formatter_->formatOption(option);
241         }
242         return;
243     }
244     if (filterType_ == eSelectOtherOptions)
245     {
246         formatter_->formatOption(option);
247         return;
248     }
249 }
250
251 /********************************************************************
252  * CommonFormatterData
253  */
254
255 class CommonFormatterData
256 {
257     public:
258         explicit CommonFormatterData(const char *timeUnit)
259             : timeUnit(timeUnit)
260         {
261         }
262
263         const char             *timeUnit;
264 };
265
266 /********************************************************************
267  * Helper functions
268  */
269
270 //! Formats option name and value.
271 void formatOptionNameAndValue(const OptionInfo &option, std::string *name,
272                               std::string *value)
273 {
274     *name  = option.name();
275     *value = "<" + option.type() + ">";
276     if (option.isType<FileNameOptionInfo>())
277     {
278         // TODO: Make these work also for other option types.
279         if (option.maxValueCount() != 1)
280         {
281             *value += " [...]";
282         }
283         if (option.minValueCount() == 0)
284         {
285             *value = "[" + *value + "]";
286         }
287     }
288     if (option.isType<BooleanOptionInfo>())
289     {
290         *name = "[no]" + *name;
291         // Old command-line parser doesn't accept any values for these.
292         // value = "[" + value + "]";
293         value->clear();
294     }
295 }
296
297 //! Formats the default option value as a string.
298 std::string
299 defaultOptionValue(const OptionInfo &option)
300 {
301     if (option.valueCount() == 0
302         || (option.valueCount() == 1 && option.formatValue(0).empty()))
303     {
304         return option.formatDefaultValueIfSet();
305     }
306     else
307     {
308         std::string result;
309         for (int i = 0; i < option.valueCount(); ++i)
310         {
311             if (i != 0)
312             {
313                 result.append(" ");
314             }
315             result.append(option.formatValue(i));
316         }
317         return result;
318     }
319 }
320
321 //! Formats the flags for a file option as a string.
322 std::string
323 fileOptionFlagsAsString(const FileNameOptionInfo &option, bool bAbbrev)
324 {
325     std::string type;
326     if (option.isInputOutputFile())
327     {
328         type = bAbbrev ? "In/Out" : "Input/Output";
329     }
330     else if (option.isInputFile())
331     {
332         type = "Input";
333     }
334     else if (option.isOutputFile())
335     {
336         type = "Output";
337     }
338     if (!option.isRequired())
339     {
340         type += bAbbrev ? ", Opt." : ", Optional";
341     }
342     if (option.isLibraryFile())
343     {
344         type += bAbbrev ? ", Lib." : ", Library";
345     }
346     return type;
347 }
348
349 //! Formats the description for an option as a string.
350 std::string
351 descriptionWithOptionDetails(const CommonFormatterData &common,
352                              const OptionInfo          &option)
353 {
354     std::string             description(option.formatDescription());
355
356     const FloatOptionInfo  *floatOption  = option.toType<FloatOptionInfo>();
357     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
358     if ((floatOption != NULL && floatOption->isTime())
359         || (doubleOption != NULL && doubleOption->isTime()))
360     {
361         // TODO: It could be nicer to have this in basicoptions.cpp.
362         description = replaceAll(description, "%t", common.timeUnit);
363     }
364
365     return description;
366 }
367
368 /********************************************************************
369  * OptionsSynopsisFormatter
370  */
371
372 /*! \brief
373  * Formatter implementation for synopsis.
374  */
375 class SynopsisFormatter : public OptionsFormatterInterface
376 {
377     public:
378         //! Creates a helper object for formatting the synopsis.
379         explicit SynopsisFormatter(const HelpWriterContext &context)
380             : context_(context), lineLength_(0), indent_(0), currentLength_(0)
381         {
382         }
383
384         //! Starts formatting the synopsis.
385         void start(const char *name);
386         //! Finishes formatting the synopsis.
387         void finish();
388
389         virtual void formatOption(const OptionInfo &option);
390
391     private:
392         const HelpWriterContext &context_;
393         int                      lineLength_;
394         int                      indent_;
395         int                      currentLength_;
396
397         GMX_DISALLOW_COPY_AND_ASSIGN(SynopsisFormatter);
398 };
399
400 void SynopsisFormatter::start(const char *name)
401 {
402     currentLength_ = std::strlen(name) + 1;
403     indent_        = std::min(currentLength_, 13);
404     File &file = context_.outputFile();
405     switch (context_.outputFormat())
406     {
407         case eHelpOutputFormat_Console:
408             lineLength_ = 78;
409             file.writeString(name);
410             break;
411         case eHelpOutputFormat_Rst:
412             lineLength_ = 74;
413             indent_    += 4;
414             file.writeLine("::");
415             file.writeLine();
416             file.writeString("    ");
417             file.writeString(name);
418             break;
419         default:
420             GMX_THROW(NotImplementedError("Synopsis formatting not implemented for this output format"));
421     }
422 }
423
424 void SynopsisFormatter::finish()
425 {
426     File &file = context_.outputFile();
427     file.writeLine();
428     file.writeLine();
429 }
430
431 void SynopsisFormatter::formatOption(const OptionInfo &option)
432 {
433     std::string name, value;
434     formatOptionNameAndValue(option, &name, &value);
435     std::string fullOptionText(" [-" + name);
436     if (!value.empty())
437     {
438         fullOptionText.append(" ");
439         fullOptionText.append(value);
440     }
441     fullOptionText.append("]");
442     const int   totalLength = fullOptionText.size();
443
444     File       &file = context_.outputFile();
445     currentLength_ += totalLength;
446     if (currentLength_ >= lineLength_)
447     {
448         file.writeString(formatString("\n%*c", indent_ - 1, ' '));
449         currentLength_ = indent_ - 1 + totalLength;
450     }
451     file.writeString(fullOptionText);
452 }
453
454 /********************************************************************
455  * OptionsListFormatter
456  */
457
458 /*! \brief
459  * Formatter implementation for help export.
460  */
461 class OptionsListFormatter : public OptionsFormatterInterface
462 {
463     public:
464         //! Creates a helper object for formatting options.
465         OptionsListFormatter(const HelpWriterContext   &context,
466                              const CommonFormatterData &common,
467                              const char                *title);
468
469         //! Initiates a new section in the options list.
470         void startSection(const char *header)
471         {
472             header_     = header;
473             bDidOutput_ = false;
474         }
475         //! Finishes a section in the options list.
476         void finishSection()
477         {
478             if (bDidOutput_)
479             {
480                 context_.writeOptionListEnd();
481                 context_.outputFile().writeLine();
482             }
483         }
484
485         virtual void formatOption(const OptionInfo &option);
486
487     private:
488         void writeSectionStartIfNecessary()
489         {
490             if (title_ != NULL)
491             {
492                 context_.writeTitle(title_);
493                 title_ = NULL;
494             }
495             if (!bDidOutput_)
496             {
497                 if (header_ != NULL)
498                 {
499                     context_.writeTextBlock(header_);
500                     context_.writeTextBlock("");
501                 }
502                 context_.writeOptionListStart();
503             }
504             bDidOutput_ = true;
505         }
506
507         const HelpWriterContext               &context_;
508         const CommonFormatterData             &common_;
509         boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
510         const char                            *title_;
511         const char                            *header_;
512         bool                                   bDidOutput_;
513
514         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsListFormatter);
515 };
516
517 OptionsListFormatter::OptionsListFormatter(
518         const HelpWriterContext   &context,
519         const CommonFormatterData &common,
520         const char                *title)
521     : context_(context), common_(common),
522       title_(title), header_(NULL), bDidOutput_(false)
523 {
524     if (context.outputFormat() == eHelpOutputFormat_Console)
525     {
526         consoleFormatter_.reset(new TextTableFormatter());
527         consoleFormatter_->setFirstColumnIndent(1);
528         consoleFormatter_->setFoldLastColumnToNextLine(4);
529         consoleFormatter_->addColumn(NULL, 6, false);
530         consoleFormatter_->addColumn(NULL, 8, false);
531         consoleFormatter_->addColumn(NULL, 10, false);
532         consoleFormatter_->addColumn(NULL, 50, true);
533     }
534 }
535
536 void OptionsListFormatter::formatOption(const OptionInfo &option)
537 {
538     writeSectionStartIfNecessary();
539     std::string name, value;
540     formatOptionNameAndValue(option, &name, &value);
541     std::string defaultValue(defaultOptionValue(option));
542     std::string info;
543     if (!defaultValue.empty())
544     {
545         info = "(" + defaultValue + ")";
546     }
547     const FileNameOptionInfo *fileOption = option.toType<FileNameOptionInfo>();
548     if (fileOption != NULL)
549     {
550         const bool bAbbrev = (context_.outputFormat() == eHelpOutputFormat_Console);
551         if (!info.empty())
552         {
553             info.append(" ");
554         }
555         info.append("(");
556         info.append(fileOptionFlagsAsString(*fileOption, bAbbrev));
557         info.append(")");
558     }
559     std::string description(descriptionWithOptionDetails(common_, option));
560     if (context_.outputFormat() == eHelpOutputFormat_Console)
561     {
562         consoleFormatter_->clear();
563         consoleFormatter_->addColumnLine(0, "-" + name);
564         consoleFormatter_->addColumnLine(1, value);
565         if (!info.empty())
566         {
567             consoleFormatter_->addColumnLine(2, info);
568         }
569         consoleFormatter_->addColumnHelpTextBlock(3, context_, description);
570         context_.outputFile().writeString(consoleFormatter_->formatRow());
571     }
572     else
573     {
574         if (!info.empty())
575         {
576             value.append(" ");
577             value.append(info);
578         }
579         context_.writeOptionItem("-" + name, value, description);
580     }
581 }
582
583 //! \}
584
585 }   // namespace
586
587 /********************************************************************
588  * CommandLineHelpWriter::Impl
589  */
590
591 /*! \internal \brief
592  * Private implementation class for CommandLineHelpWriter.
593  *
594  * \ingroup module_commandline
595  */
596 class CommandLineHelpWriter::Impl
597 {
598     public:
599         //! Sets the Options object to use for generating help.
600         explicit Impl(const Options &options);
601
602         //! Format the list of known issues.
603         void formatBugs(const HelpWriterContext &context);
604
605         //! Options object to use for generating help.
606         const Options               &options_;
607         //! List of bugs/knows issues.
608         ConstArrayRef<const char *>  bugs_;
609         //! Time unit to show in descriptions.
610         std::string                  timeUnit_;
611         //! Whether to write descriptions to output.
612         bool                         bShowDescriptions_;
613 };
614
615 CommandLineHelpWriter::Impl::Impl(const Options &options)
616     : options_(options), timeUnit_(TimeUnitManager().timeUnitAsString()),
617       bShowDescriptions_(false)
618 {
619 }
620
621 void CommandLineHelpWriter::Impl::formatBugs(const HelpWriterContext &context)
622 {
623     if (bugs_.empty())
624     {
625         return;
626     }
627     context.writeTitle("Known Issues");
628     ConstArrayRef<const char *>::const_iterator i;
629     for (i = bugs_.begin(); i != bugs_.end(); ++i)
630     {
631         const char *const       bug = *i;
632         TextLineWrapperSettings settings;
633         settings.setIndent(2);
634         settings.setFirstLineIndent(0);
635         settings.setLineLength(78);
636         context.outputFile().writeLine(
637                 context.substituteMarkupAndWrapToString(
638                         settings, formatString("* %s", bug)));
639     }
640 }
641
642
643 /********************************************************************
644  * CommandLineHelpWriter
645  */
646
647 CommandLineHelpWriter::CommandLineHelpWriter(const Options &options)
648     : impl_(new Impl(options))
649 {
650 }
651
652 CommandLineHelpWriter::~CommandLineHelpWriter()
653 {
654 }
655
656 CommandLineHelpWriter &
657 CommandLineHelpWriter::setShowDescriptions(bool bSet)
658 {
659     impl_->bShowDescriptions_ = bSet;
660     return *this;
661 }
662
663 CommandLineHelpWriter &
664 CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
665 {
666     impl_->timeUnit_ = timeUnit;
667     return *this;
668 }
669
670 CommandLineHelpWriter &
671 CommandLineHelpWriter::setKnownIssues(const ConstArrayRef<const char *> &bugs)
672 {
673     impl_->bugs_ = bugs;
674     return *this;
675 }
676
677 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
678 {
679     if (context.isCompletionExport())
680     {
681         context.shellCompletionWriter().writeModuleCompletions(
682                 context.moduleDisplayName(), impl_->options_);
683         return;
684     }
685     const HelpWriterContext &writerContext = context.writerContext();
686     OptionsFilter            filter;
687     filter.setShowHidden(context.showHidden());
688
689     {
690         writerContext.writeTitle("Synopsis");
691         SynopsisFormatter synopsisFormatter(writerContext);
692         synopsisFormatter.start(context.moduleDisplayName());
693         filter.formatSelected(OptionsFilter::eSelectFileOptions,
694                               &synopsisFormatter, impl_->options_);
695         filter.formatSelected(OptionsFilter::eSelectOtherOptions,
696                               &synopsisFormatter, impl_->options_);
697         synopsisFormatter.finish();
698     }
699
700     if (impl_->bShowDescriptions_)
701     {
702         DescriptionsFormatter descriptionFormatter(writerContext);
703         descriptionFormatter.format(impl_->options_, "Description");
704     }
705     CommonFormatterData    common(impl_->timeUnit_.c_str());
706     OptionsListFormatter   formatter(writerContext, common, "Options");
707     formatter.startSection("Options to specify input and output files:[PAR]");
708     filter.formatSelected(OptionsFilter::eSelectFileOptions,
709                           &formatter, impl_->options_);
710     formatter.finishSection();
711     formatter.startSection("Other options:[PAR]");
712     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
713                           &formatter, impl_->options_);
714     formatter.finishSection();
715
716     impl_->formatBugs(writerContext);
717 }
718
719 } // namespace gmx