Merge remote-tracking branch 'origin/release-4-6' into HEAD
[alexxy/gromacs.git] / src / gromacs / commandline / cmdlinehelpwriter.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements gmx::CommandLineHelpWriter.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_commandline
37  */
38 #include "cmdlinehelpwriter.h"
39
40 #include <string>
41
42 #include "gromacs/onlinehelp/helpformat.h"
43 #include "gromacs/onlinehelp/helpwritercontext.h"
44 #include "gromacs/options/basicoptioninfo.h"
45 #include "gromacs/options/filenameoptioninfo.h"
46 #include "gromacs/options/options.h"
47 #include "gromacs/options/optionsvisitor.h"
48 #include "gromacs/options/timeunitmanager.h"
49 #include "gromacs/selection/selectionfileoptioninfo.h"
50 #include "gromacs/selection/selectionoptioninfo.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/file.h"
53 #include "gromacs/utility/stringutil.h"
54
55 namespace gmx
56 {
57
58 namespace
59 {
60
61 /********************************************************************
62  * DescriptionWriter
63  */
64
65 /*! \internal \brief
66  * Helper object for writing section descriptions to help.
67  *
68  * \ingroup module_commandline
69  */
70 class DescriptionWriter : public OptionsVisitor
71 {
72     public:
73         //! Creates a helper object for writing section descriptions.
74         explicit DescriptionWriter(const HelpWriterContext &context)
75             : context_(context)
76         {
77         }
78
79         virtual void visitSubSection(const Options &section);
80         virtual void visitOption(const OptionInfo & /*option*/) { }
81
82     private:
83         const HelpWriterContext &context_;
84 };
85
86 void DescriptionWriter::visitSubSection(const Options &section)
87 {
88     if (!section.description().empty())
89     {
90         File &file = context_.outputFile();
91         const std::string &title = section.title();
92         if (!title.empty())
93         {
94             file.writeLine(title);
95             file.writeLine();
96         }
97         context_.writeTextBlock(section.description());
98         file.writeLine();
99     }
100     OptionsIterator(section).acceptSubSections(this);
101 }
102
103
104 /********************************************************************
105  * FileParameterWriter
106  */
107
108 /*! \internal \brief
109  * Helper object for writing help for file parameters.
110  *
111  * \ingroup module_commandline
112  */
113 class FileParameterWriter : public OptionsTypeVisitor<FileNameOptionInfo>
114 {
115     public:
116         //! Creates a helper object for writing file parameters.
117         explicit FileParameterWriter(const HelpWriterContext &context);
118
119         //! Returns true if anything was written out.
120         bool didOutput() const { return formatter_.didOutput(); }
121
122         virtual void visitSubSection(const Options &section);
123         virtual void visitOptionType(const FileNameOptionInfo &option);
124
125     private:
126         const HelpWriterContext &context_;
127         TextTableFormatter       formatter_;
128 };
129
130 FileParameterWriter::FileParameterWriter(const HelpWriterContext &context)
131     : context_(context)
132 {
133     formatter_.addColumn("Option",      6, false);
134     formatter_.addColumn("Filename",    12, false);
135     formatter_.addColumn("Type",        12, false);
136     formatter_.addColumn("Description", 45, true);
137 }
138
139 void FileParameterWriter::visitSubSection(const Options &section)
140 {
141     OptionsIterator iterator(section);
142     iterator.acceptSubSections(this);
143     iterator.acceptOptions(this);
144 }
145
146 void FileParameterWriter::visitOptionType(const FileNameOptionInfo &option)
147 {
148     int firstShortValue = 0; // The first value after which the type fits.
149     int firstLongValue = -1; // First value that overlaps description column.
150     int lastLongValue = -1;  // Last value like the above.
151
152     // Get the values to write and check where text overflows the columns.
153     formatter_.clear();
154     std::string name(formatString("-%s", option.name().c_str()));
155     formatter_.addColumnLine(0, name);
156     for (int i = 0; i < option.valueCount() || i == 0; ++i)
157     {
158         std::string value;
159         if (option.valueCount() == 0
160             || (option.valueCount() == 1 && option.formatValue(0).empty()))
161         {
162             value = option.formatDefaultValueIfSet();
163         }
164         else
165         {
166             value = option.formatValue(i);
167         }
168         formatter_.addColumnLine(1, value);
169         if (value.length() > 12U && i == firstShortValue)
170         {
171             firstShortValue = i + 1;
172         }
173         if (value.length() > 25U)
174         {
175             if (firstLongValue == -1)
176             {
177                 firstLongValue = i;
178             }
179             lastLongValue = i;
180         }
181     }
182     std::string type;
183     if (option.isInputOutputFile())
184     {
185         type = "In/Out";
186     }
187     else if (option.isInputFile())
188     {
189         type = "Input";
190     }
191     else if (option.isOutputFile())
192     {
193         type = "Output";
194     }
195     if (!option.isRequired())
196     {
197         type += ", Opt.";
198     }
199     if (option.isLibraryFile())
200     {
201         type += ", Lib.";
202     }
203     bool bLongType = (type.length() > 12U);
204     formatter_.addColumnLine(2, type);
205     formatter_.addColumnLine(3, context_.substituteMarkup(option.description()));
206
207     // Compute layout.
208     if (name.length() > 6U || firstShortValue > 0)
209     {
210         formatter_.setColumnFirstLineOffset(1, 1);
211         // Assume that the name is <20 chars, so that the type fits
212         if (firstLongValue >= 0)
213         {
214             ++firstLongValue;
215             ++lastLongValue;
216         }
217     }
218     int firstDescriptionLine = 0;
219     if (bLongType)
220     {
221         firstDescriptionLine = 1;
222     }
223     formatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
224     if (firstLongValue >= 0 && formatter_.lastColumnLine(3) >= firstLongValue)
225     {
226         firstDescriptionLine = lastLongValue + 1;
227         formatter_.setColumnFirstLineOffset(3, firstDescriptionLine);
228     }
229
230     // Do the formatting.
231     context_.outputFile().writeString(formatter_.formatRow());
232 }
233
234
235 /********************************************************************
236  * ParameterWriter
237  */
238
239 /*! \internal \brief
240  * Helper object for writing help for non-file parameters.
241  *
242  * \ingroup module_commandline
243  */
244 class ParameterWriter : public OptionsVisitor
245 {
246     public:
247         //! Creates a helper object for writing non-file parameters.
248         ParameterWriter(const HelpWriterContext &context,
249                         const char *timeUnit);
250
251         //! Sets the writer to show hidden options.
252         void setShowHidden(bool bSet) { bShowHidden_ = bSet; }
253         //! Returns true if anything was written out.
254         bool didOutput() const { return formatter_.didOutput(); }
255
256         virtual void visitSubSection(const Options &section);
257         virtual void visitOption(const OptionInfo &option);
258
259     private:
260         const HelpWriterContext &context_;
261         TextTableFormatter       formatter_;
262         const char              *timeUnit_;
263         bool                     bShowHidden_;
264 };
265
266 ParameterWriter::ParameterWriter(const HelpWriterContext &context,
267                                  const char *timeUnit)
268     : context_(context), timeUnit_(timeUnit), bShowHidden_(false)
269 {
270     formatter_.addColumn("Option",      12, false);
271     formatter_.addColumn("Type",         6, false);
272     formatter_.addColumn("Value",        6, false);
273     formatter_.addColumn("Description", 51, true);
274 }
275
276 void ParameterWriter::visitSubSection(const Options &section)
277 {
278     OptionsIterator iterator(section);
279     iterator.acceptSubSections(this);
280     iterator.acceptOptions(this);
281 }
282
283 void ParameterWriter::visitOption(const OptionInfo &option)
284 {
285     if (option.isType<FileNameOptionInfo>()
286         || option.isType<SelectionFileOptionInfo>()
287         || option.isType<SelectionOptionInfo>()
288         || (!bShowHidden_ && option.isHidden()))
289     {
290         return;
291     }
292
293     formatter_.clear();
294     bool bIsBool = option.isType<BooleanOptionInfo>();
295     std::string name(formatString("-%s%s", bIsBool ? "[no]" : "",
296                                            option.name().c_str()));
297     formatter_.addColumnLine(0, name);
298     formatter_.addColumnLine(1, option.type());
299     if (name.length() > 12U)
300     {
301         formatter_.setColumnFirstLineOffset(1, 1);
302     }
303     // TODO: Better handling of multiple long values
304     std::string values;
305     for (int i = 0; i < option.valueCount(); ++i)
306     {
307         if (i != 0)
308         {
309             values.append(" ");
310         }
311         values.append(option.formatValue(i));
312     }
313     formatter_.addColumnLine(2, values);
314     std::string description(context_.substituteMarkup(option.description()));
315     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
316     if (doubleOption != NULL && doubleOption->isTime())
317     {
318         description = replaceAll(description, "%t", timeUnit_);
319     }
320     formatter_.addColumnLine(3, description);
321     if (values.length() > 6U)
322     {
323         formatter_.setColumnFirstLineOffset(3, 1);
324     }
325
326     context_.outputFile().writeString(formatter_.formatRow());
327 }
328
329
330 /********************************************************************
331  * SelectionParameterWriter
332  */
333
334 /*! \internal \brief
335  * Helper object for writing help for selection parameters.
336  *
337  * \ingroup module_commandline
338  */
339 class SelectionParameterWriter : public OptionsVisitor
340 {
341     public:
342         //! Creates a helper object for writing selection parameters.
343         explicit SelectionParameterWriter(const HelpWriterContext &context);
344
345         //! Returns true if anything was written out.
346         bool didOutput() const { return formatter_.didOutput(); }
347
348         virtual void visitSubSection(const Options &section);
349         virtual void visitOption(const OptionInfo &option);
350
351     private:
352         const HelpWriterContext &context_;
353         TextTableFormatter       formatter_;
354 };
355
356 SelectionParameterWriter::SelectionParameterWriter(const HelpWriterContext &context)
357     : context_(context)
358 {
359     formatter_.addColumn("Selection",   10, false);
360     formatter_.addColumn("Description", 67, true);
361 }
362
363 void SelectionParameterWriter::visitSubSection(const Options &section)
364 {
365     OptionsIterator iterator(section);
366     iterator.acceptSubSections(this);
367     iterator.acceptOptions(this);
368 }
369
370 void SelectionParameterWriter::visitOption(const OptionInfo &option)
371 {
372     if (!option.isType<SelectionFileOptionInfo>()
373         && !option.isType<SelectionOptionInfo>())
374     {
375         return;
376     }
377
378     File &file = context_.outputFile();
379
380     formatter_.clear();
381     std::string name(formatString("-%s", option.name().c_str()));
382     formatter_.addColumnLine(0, name);
383     formatter_.addColumnLine(1, context_.substituteMarkup(option.description()));
384     file.writeString(formatter_.formatRow());
385
386     // TODO: What to do with selection variables?
387     // They are not printed as values for any option.
388     for (int i = 0; i < option.valueCount(); ++i)
389     {
390         std::string value(option.formatValue(i));
391         // TODO: Wrapping
392         file.writeLine(formatString("    %s", value.c_str()));
393     }
394 }
395
396 } // namespace
397
398 /********************************************************************
399  * CommandLineHelpWriter::Impl
400  */
401
402 /*! \internal \brief
403  * Private implementation class for CommandLineHelpWriter.
404  *
405  * \ingroup module_commandline
406  */
407 class CommandLineHelpWriter::Impl
408 {
409     public:
410         //! Sets the Options object to use for generating help.
411         explicit Impl(const Options &options);
412
413         //! Options object to use for generating help.
414         const Options          &options_;
415         //! Time unit to show in descriptions.
416         std::string             timeUnit_;
417         //! Whether to write descriptions to output.
418         bool                    bShowDescriptions_;
419         //! Whether to write hidden options to output.
420         bool                    bShowHidden_;
421 };
422
423 CommandLineHelpWriter::Impl::Impl(const Options &options)
424     : options_(options), timeUnit_(TimeUnitManager().timeUnitAsString()),
425       bShowDescriptions_(false), bShowHidden_(false)
426 {
427 }
428
429 /********************************************************************
430  * CommandLineHelpWriter
431  */
432
433 CommandLineHelpWriter::CommandLineHelpWriter(const Options &options)
434     : impl_(new Impl(options))
435 {
436 }
437
438 CommandLineHelpWriter::~CommandLineHelpWriter()
439 {
440 }
441
442 CommandLineHelpWriter &CommandLineHelpWriter::setShowHidden(bool bSet)
443 {
444     impl_->bShowHidden_ = bSet;
445     return *this;
446 }
447
448 CommandLineHelpWriter &CommandLineHelpWriter::setShowDescriptions(bool bSet)
449 {
450     impl_->bShowDescriptions_ = bSet;
451     return *this;
452 }
453
454 CommandLineHelpWriter &CommandLineHelpWriter::setTimeUnitString(const char *timeUnit)
455 {
456     impl_->timeUnit_ = timeUnit;
457     return *this;
458 }
459
460 void CommandLineHelpWriter::writeHelp(const HelpWriterContext &context)
461 {
462     if (context.outputFormat() != eHelpOutputFormat_Console)
463     {
464         // TODO: Implement once the situation with Redmine issue #969 is more
465         // clear.
466         GMX_THROW(NotImplementedError(
467                     "Command-line help is not implemented for this output format"));
468     }
469     File &file = context.outputFile();
470     if (impl_->bShowDescriptions_)
471     {
472         file.writeLine("DESCRIPTION");
473         file.writeLine("-----------");
474         file.writeLine();
475         DescriptionWriter(context).visitSubSection(impl_->options_);
476     }
477     {
478         FileParameterWriter writer(context);
479         writer.visitSubSection(impl_->options_);
480         if (writer.didOutput())
481         {
482             file.writeLine();
483         }
484     }
485     {
486         ParameterWriter writer(context, impl_->timeUnit_.c_str());
487         writer.setShowHidden(impl_->bShowHidden_);
488         writer.visitSubSection(impl_->options_);
489         if (writer.didOutput())
490         {
491             file.writeLine();
492         }
493     }
494     {
495         SelectionParameterWriter writer(context);
496         writer.visitSubSection(impl_->options_);
497         if (writer.didOutput())
498         {
499             file.writeLine();
500         }
501     }
502 }
503
504 } // namespace gmx