Sort all includes in src/gromacs
[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, 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_Man:
412             lineLength_ = 70;
413             file.writeString(name);
414             break;
415         case eHelpOutputFormat_Html:
416             lineLength_ = 78;
417             file.writeLine("<pre>");
418             file.writeString(name);
419             break;
420         default:
421             GMX_THROW(NotImplementedError("Synopsis formatting not implemented for this output format"));
422     }
423 }
424
425 void SynopsisFormatter::finish()
426 {
427     File &file = context_.outputFile();
428     file.writeLine();
429     if (context_.outputFormat() == eHelpOutputFormat_Html)
430     {
431         file.writeLine("</pre>");
432     }
433     file.writeLine();
434 }
435
436 void SynopsisFormatter::formatOption(const OptionInfo &option)
437 {
438     std::string name, value;
439     formatOptionNameAndValue(option, &name, &value);
440     std::string fullOptionText(" [-" + name);
441     if (!value.empty())
442     {
443         fullOptionText.append(" ");
444         fullOptionText.append(value);
445     }
446     fullOptionText.append("]");
447     const int   totalLength = fullOptionText.size();
448
449     if (context_.outputFormat() == eHelpOutputFormat_Html)
450     {
451         value = replaceAll(value, "<", "&lt;");
452         value = replaceAll(value, ">", "&gt;");
453     }
454
455     File &file = context_.outputFile();
456     currentLength_ += totalLength;
457     if (currentLength_ >= lineLength_)
458     {
459         file.writeString(formatString("\n%*c", indent_ - 1, ' '));
460         currentLength_ = indent_ - 1 + totalLength;
461     }
462     file.writeString(fullOptionText);
463 }
464
465 /********************************************************************
466  * OptionsListFormatter
467  */
468
469 /*! \brief
470  * Formatter implementation for help export.
471  */
472 class OptionsListFormatter : public OptionsFormatterInterface
473 {
474     public:
475         //! Creates a helper object for formatting options.
476         OptionsListFormatter(const HelpWriterContext   &context,
477                              const CommonFormatterData &common,
478                              const char                *title);
479
480         //! Initiates a new section in the options list.
481         void startSection(const char *header)
482         {
483             header_     = header;
484             bDidOutput_ = false;
485         }
486         //! Finishes a section in the options list.
487         void finishSection()
488         {
489             if (bDidOutput_)
490             {
491                 context_.writeOptionListEnd();
492                 context_.outputFile().writeLine();
493             }
494         }
495
496         virtual void formatOption(const OptionInfo &option);
497
498     private:
499         void writeSectionStartIfNecessary()
500         {
501             if (title_ != NULL)
502             {
503                 context_.writeTitle(title_);
504                 title_ = NULL;
505             }
506             if (!bDidOutput_)
507             {
508                 if (header_ != NULL)
509                 {
510                     context_.writeTextBlock(header_);
511                 }
512                 context_.writeOptionListStart();
513             }
514             bDidOutput_ = true;
515         }
516
517         const HelpWriterContext               &context_;
518         const CommonFormatterData             &common_;
519         boost::scoped_ptr<TextTableFormatter>  consoleFormatter_;
520         const char                            *title_;
521         const char                            *header_;
522         bool                                   bDidOutput_;
523
524         GMX_DISALLOW_COPY_AND_ASSIGN(OptionsListFormatter);
525 };
526
527 OptionsListFormatter::OptionsListFormatter(
528         const HelpWriterContext   &context,
529         const CommonFormatterData &common,
530         const char                *title)
531     : context_(context), common_(common),
532       title_(title), header_(NULL), bDidOutput_(false)
533 {
534     if (context.outputFormat() == eHelpOutputFormat_Console)
535     {
536         consoleFormatter_.reset(new TextTableFormatter());
537         consoleFormatter_->setFirstColumnIndent(1);
538         consoleFormatter_->setFoldLastColumnToNextLine(4);
539         consoleFormatter_->addColumn(NULL, 6, false);
540         consoleFormatter_->addColumn(NULL, 8, false);
541         consoleFormatter_->addColumn(NULL, 10, false);
542         consoleFormatter_->addColumn(NULL, 50, true);
543     }
544 }
545
546 void OptionsListFormatter::formatOption(const OptionInfo &option)
547 {
548     writeSectionStartIfNecessary();
549     std::string name, value;
550     formatOptionNameAndValue(option, &name, &value);
551     std::string defaultValue(defaultOptionValue(option));
552     std::string info;
553     if (!defaultValue.empty())
554     {
555         info = "(" + defaultValue + ")";
556     }
557     const FileNameOptionInfo *fileOption = option.toType<FileNameOptionInfo>();
558     if (fileOption != NULL)
559     {
560         const bool bAbbrev = (context_.outputFormat() == eHelpOutputFormat_Console);
561         if (!info.empty())
562         {
563             info.append(" ");
564         }
565         info.append("(");
566         info.append(fileOptionFlagsAsString(*fileOption, bAbbrev));
567         info.append(")");
568     }
569     std::string description(descriptionWithOptionDetails(common_, option));
570     if (context_.outputFormat() == eHelpOutputFormat_Console)
571     {
572         consoleFormatter_->clear();
573         consoleFormatter_->addColumnLine(0, "-" + name);
574         consoleFormatter_->addColumnLine(1, value);
575         if (!info.empty())
576         {
577             consoleFormatter_->addColumnLine(2, info);
578         }
579         consoleFormatter_->addColumnHelpTextBlock(3, context_, description);
580         context_.outputFile().writeString(consoleFormatter_->formatRow());
581     }
582     else
583     {
584         if (!info.empty())
585         {
586             value.append(" ");
587             value.append(info);
588         }
589         context_.writeOptionItem("-" + name, value, description);
590     }
591 }
592
593 //! \}
594
595 }   // namespace
596
597 /********************************************************************
598  * CommandLineHelpWriter::Impl
599  */
600
601 /*! \internal \brief
602  * Private implementation class for CommandLineHelpWriter.
603  *
604  * \ingroup module_commandline
605  */
606 class CommandLineHelpWriter::Impl
607 {
608     public:
609         //! Sets the Options object to use for generating help.
610         explicit Impl(const Options &options);
611
612         //! Format the list of known issues.
613         void formatBugs(const HelpWriterContext &context);
614
615         //! Options object to use for generating help.
616         const Options               &options_;
617         //! List of bugs/knows issues.
618         ConstArrayRef<const char *>  bugs_;
619         //! Time unit to show in descriptions.
620         std::string                  timeUnit_;
621         //! Whether to write descriptions to output.
622         bool                         bShowDescriptions_;
623 };
624
625 CommandLineHelpWriter::Impl::Impl(const Options &options)
626     : options_(options), timeUnit_(TimeUnitManager().timeUnitAsString()),
627       bShowDescriptions_(false)
628 {
629 }
630
631 void CommandLineHelpWriter::Impl::formatBugs(const HelpWriterContext &context)
632 {
633     if (bugs_.empty())
634     {
635         return;
636     }
637     context.writeTitle("Known Issues");
638     if (context.outputFormat() != eHelpOutputFormat_Console)
639     {
640         context.writeTextBlock("[UL]");
641     }
642     ConstArrayRef<const char *>::const_iterator i;
643     for (i = bugs_.begin(); i != bugs_.end(); ++i)
644     {
645         const char *const bug = *i;
646         // TODO: The context should be able to do this also for console output, but
647         // that requires a lot more elaborate parser for the markup.
648         if (context.outputFormat() == eHelpOutputFormat_Console)
649         {
650             TextLineWrapperSettings settings;
651             settings.setIndent(2);
652             settings.setFirstLineIndent(0);
653             settings.setLineLength(78);
654             context.outputFile().writeLine(
655                     context.substituteMarkupAndWrapToString(
656                             settings, formatString("* %s", bug)));
657         }
658         else
659         {
660             context.writeTextBlock(formatString("[LI]%s", bug));
661         }
662     }
663     if (context.outputFormat() != eHelpOutputFormat_Console)
664     {
665         context.writeTextBlock("[ul]");
666     }
667 }
668
669
670 /********************************************************************
671  * CommandLineHelpWriter
672  */
673
674 CommandLineHelpWriter::CommandLineHelpWriter(const Options &options)
675     : impl_(new Impl(options))
676 {
677 }
678
679 CommandLineHelpWriter::~CommandLineHelpWriter()
680 {
681 }
682
683 CommandLineHelpWriter &
684 CommandLineHelpWriter::setShowDescriptions(bool bSet)
685 {
686     impl_->bShowDescriptions_ = bSet;
687     return *this;
688 }
689
690 CommandLineHelpWriter &
691 CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
692 {
693     impl_->timeUnit_ = timeUnit;
694     return *this;
695 }
696
697 CommandLineHelpWriter &
698 CommandLineHelpWriter::setKnownIssues(const ConstArrayRef<const char *> &bugs)
699 {
700     impl_->bugs_ = bugs;
701     return *this;
702 }
703
704 void CommandLineHelpWriter::writeHelp(const CommandLineHelpContext &context)
705 {
706     if (context.isCompletionExport())
707     {
708         context.shellCompletionWriter().writeModuleCompletions(
709                 context.moduleDisplayName(), impl_->options_);
710         return;
711     }
712     const HelpWriterContext &writerContext = context.writerContext();
713     OptionsFilter            filter;
714     filter.setShowHidden(context.showHidden());
715
716     {
717         writerContext.writeTitle("Synopsis");
718         SynopsisFormatter synopsisFormatter(writerContext);
719         synopsisFormatter.start(context.moduleDisplayName());
720         filter.formatSelected(OptionsFilter::eSelectFileOptions,
721                               &synopsisFormatter, impl_->options_);
722         filter.formatSelected(OptionsFilter::eSelectOtherOptions,
723                               &synopsisFormatter, impl_->options_);
724         synopsisFormatter.finish();
725     }
726
727     if (impl_->bShowDescriptions_)
728     {
729         DescriptionsFormatter descriptionFormatter(writerContext);
730         descriptionFormatter.format(impl_->options_, "Description");
731     }
732     CommonFormatterData    common(impl_->timeUnit_.c_str());
733     OptionsListFormatter   formatter(writerContext, common, "Options");
734     formatter.startSection("Options to specify input and output files:[PAR]");
735     filter.formatSelected(OptionsFilter::eSelectFileOptions,
736                           &formatter, impl_->options_);
737     formatter.finishSection();
738     formatter.startSection("Other options:[PAR]");
739     filter.formatSelected(OptionsFilter::eSelectOtherOptions,
740                           &formatter, impl_->options_);
741     formatter.finishSection();
742
743     impl_->formatBugs(writerContext);
744 }
745
746 } // namespace gmx