2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 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.
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.
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.
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.
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.
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.
37 * Implements gmx::HelpWriterContext.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_onlinehelp
44 #include "helpwritercontext.h"
52 #include <boost/shared_ptr.hpp>
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/file.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/programcontext.h"
58 #include "gromacs/utility/stringutil.h"
66 //! \internal \addtogroup module_onlinehelp
75 /* The order of these arrays is significant. Text search and replace
76 * for each element occurs in order, so earlier changes can induce
77 * subsequent changes even though the original text might not appear
78 * to invoke the latter changes.
79 * TODO: Get rid of this behavior. It makes it very difficult to manage
80 * replacements coming from multiple sources (e.g., hyperlinks).*/
82 //! List of replacements for console output.
83 const t_sandr sandrTty[] = {
100 { "[INT]", "integral" },
101 { "[FROM]", " from " },
110 { "[SQRT]", "sqrt(" },
124 { "[COSH]", "cosh(" },
126 { "[SINH]", "sinh(" },
128 { "[TANH]", "tanh(" },
136 //! List of replacements for reStructuredText output.
137 const t_sandr sandrRst[] = {
146 { "[CHEVRON]", "<" },
147 { "[chevron]", ">" },
150 { "[INT]", "integral" },
151 { "[FROM]", " from " },
160 { "[SQRT]", "sqrt(" },
174 { "[COSH]", "cosh(" },
176 { "[SINH]", "sinh(" },
178 { "[TANH]", "tanh(" },
181 // [BR] is fundamentally incompatible with rst
188 * Replaces all entries from a list of replacements.
190 std::string repall(const std::string &s, int nsr, const t_sandr sa[])
192 std::string result(s);
193 for (int i = 0; i < nsr; ++i)
195 result = replaceAll(result, sa[i].search, sa[i].replace);
201 * Replaces all entries from a list of replacements.
203 template <size_t nsr>
204 std::string repall(const std::string &s, const t_sandr (&sa)[nsr])
206 return repall(s, nsr, sa);
210 * Custom output interface for HelpWriterContext::Impl::processMarkup().
212 * Provides an interface that is used to implement different types of output
213 * from HelpWriterContext::Impl::processMarkup().
215 class WrapperInterface
218 virtual ~WrapperInterface() {}
221 * Provides the wrapping settings.
223 * HelpWriterContext::Impl::processMarkup() may provide some default
224 * values for the settings if they are not set; this is the reason the
225 * return value is not const.
227 virtual TextLineWrapperSettings &settings() = 0;
228 //! Appends the given string to output.
229 virtual void wrap(const std::string &text) = 0;
233 * Wraps markup output into a single string.
235 class WrapperToString : public WrapperInterface
238 //! Creates a wrapper with the given settings.
239 explicit WrapperToString(const TextLineWrapperSettings &settings)
244 virtual TextLineWrapperSettings &settings()
246 return wrapper_.settings();
248 virtual void wrap(const std::string &text)
250 result_.append(wrapper_.wrapToString(text));
252 //! Returns the result string.
253 const std::string &result() const { return result_; }
256 TextLineWrapper wrapper_;
261 * Wraps markup output into a vector of string (one line per element).
263 class WrapperToVector : public WrapperInterface
266 //! Creates a wrapper with the given settings.
267 explicit WrapperToVector(const TextLineWrapperSettings &settings)
272 virtual TextLineWrapperSettings &settings()
274 return wrapper_.settings();
276 virtual void wrap(const std::string &text)
278 const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
279 result_.insert(result_.end(), lines.begin(), lines.end());
281 //! Returns a vector with the output lines.
282 const std::vector<std::string> &result() const { return result_; }
285 TextLineWrapper wrapper_;
286 std::vector<std::string> result_;
290 * Makes the string uppercase.
292 * \param[in] text Input text.
293 * \returns \p text with all characters transformed to uppercase.
294 * \throws std::bad_alloc if out of memory.
296 std::string toUpperCase(const std::string &text)
298 std::string result(text);
299 std::transform(result.begin(), result.end(), result.begin(), toupper);
304 * Removes extra newlines from reStructuredText.
306 * \param[in] text Input text.
307 * \returns \p text with all sequences of more than two newlines replaced
308 * with just two newlines.
312 std::string removeExtraNewlinesRst(const std::string &text)
314 // Start from 2, so that all newlines in the beginning get stripped off.
315 int newlineCount = 2;
317 result.reserve(text.length());
318 for (size_t i = 0; i < text.length(); ++i)
323 if (newlineCount > 2)
332 result.push_back(text[i]);
334 size_t last = result.find_last_not_of('\n');
335 if (last != std::string::npos)
337 result.resize(last + 1);
346 /********************************************************************
351 * Private implementation class for HelpLinks.
353 * \ingroup module_onlinehelp
355 class HelpLinks::Impl
360 LinkItem(const std::string &linkName,
361 const std::string &replacement)
362 : linkName(linkName), replacement(replacement)
365 std::string linkName;
366 std::string replacement;
369 //! Shorthand for a list of links.
370 typedef std::vector<LinkItem> LinkList;
372 //! Initializes empty links with the given format.
373 explicit Impl(HelpOutputFormat format) : format_(format)
379 //! Output format for which the links are formatted.
380 HelpOutputFormat format_;
383 /********************************************************************
387 HelpLinks::HelpLinks(HelpOutputFormat format) : impl_(new Impl(format))
391 HelpLinks::~HelpLinks()
395 void HelpLinks::addLink(const std::string &linkName,
396 const std::string &targetName,
397 const std::string &displayName)
399 std::string replacement;
400 switch (impl_->format_)
402 case eHelpOutputFormat_Console:
403 replacement = repall(displayName, sandrTty);
405 case eHelpOutputFormat_Rst:
406 replacement = targetName;
409 GMX_RELEASE_ASSERT(false, "Output format not implemented for links");
411 impl_->links_.push_back(Impl::LinkItem(linkName, replacement));
414 /********************************************************************
415 * HelpWriterContext::Impl
419 * Private implementation class for HelpWriterContext.
421 * \ingroup module_onlinehelp
423 class HelpWriterContext::Impl
427 * Shared, non-modifiable state for context objects.
429 * Contents of this structure are shared between all context objects
430 * that are created from a common parent.
431 * This state should not be modified after construction.
433 * \ingroup module_onlinehelp
437 //! Initializes the state with the given parameters.
438 SharedState(File *file, HelpOutputFormat format,
439 const HelpLinks *links)
440 : file_(*file), format_(format), links_(links)
444 //! Output file to which the help is written.
446 //! Output format for the help output.
447 HelpOutputFormat format_;
449 const HelpLinks *links_;
454 ReplaceItem(const std::string &search,
455 const std::string &replace)
456 : search(search), replace(replace)
463 //! Smart pointer type for managing the shared state.
464 typedef boost::shared_ptr<const SharedState> StatePointer;
465 //! Shorthand for a list of markup/other replacements.
466 typedef std::vector<ReplaceItem> ReplaceList;
468 //! Initializes the context with the given state.
469 explicit Impl(const StatePointer &state)
472 initDefaultReplacements();
475 //! Initializes default replacements for the chosen output format.
476 void initDefaultReplacements();
477 //! Adds a new replacement.
478 void addReplacement(const std::string &search,
479 const std::string &replace)
481 replacements_.push_back(ReplaceItem(search, replace));
484 //! Replaces links in a given string.
485 std::string replaceLinks(const std::string &input) const;
488 * Process markup and wrap lines within a block of text.
490 * \param[in] text Text to process.
491 * \param wrapper Object used to wrap the text.
493 * The \p wrapper should take care of either writing the text to output
494 * or providing an interface for the caller to retrieve the output.
496 void processMarkup(const std::string &text,
497 WrapperInterface *wrapper) const;
499 //! Constant state shared by all child context objects.
501 //! List of markup/other replacements.
502 ReplaceList replacements_;
505 GMX_DISALLOW_ASSIGN(Impl);
508 void HelpWriterContext::Impl::initDefaultReplacements()
510 const char *program = getProgramContext().programName();
511 addReplacement("[PROGRAM]", program);
514 std::string HelpWriterContext::Impl::replaceLinks(const std::string &input) const
516 std::string result(input);
517 if (state_->links_ != NULL)
519 HelpLinks::Impl::LinkList::const_iterator link;
520 for (link = state_->links_->impl_->links_.begin();
521 link != state_->links_->impl_->links_.end(); ++link)
523 result = replaceAllWords(result, link->linkName, link->replacement);
529 void HelpWriterContext::Impl::processMarkup(const std::string &text,
530 WrapperInterface *wrapper) const
532 std::string result(text);
533 for (ReplaceList::const_iterator i = replacements_.begin();
534 i != replacements_.end(); ++i)
536 result = replaceAll(result, i->search, i->replace);
538 switch (state_->format_)
540 case eHelpOutputFormat_Console:
542 const int baseFirstLineIndent = wrapper->settings().firstLineIndent();
543 const int baseIndent = wrapper->settings().indent();
544 result = repall(result, sandrTty);
545 result = replaceLinks(result);
546 std::string paragraph;
547 paragraph.reserve(result.length());
549 int nextBreakSize = 0;
550 bool bLiteral = false;
551 while (i < result.length())
553 while (i < result.length() && result[i] == '\n')
557 if (i == result.length())
561 const int breakSize = nextBreakSize;
563 bool bLineStart = true;
564 int currentIndent = 0;
570 if (result[i] == '\n' || i == result.length())
572 if (currentLine == 0)
574 firstIndent = currentIndent;
576 else if (currentLine == 1)
578 indent = currentIndent;
583 if (i + 1 >= result.length() || result[i + 1] == '\n')
590 if (!std::isspace(result[i - 1]))
592 paragraph.push_back(' ');
599 if (std::isspace(result[i]))
604 else if (i + 1 < result.length()
605 && result[i] == '*' && result[i + 1] == ' ')
609 while (i > 0 && result[i - 1] != '\n')
613 paragraph = stripString(paragraph);
617 indent = currentIndent + 2;
621 paragraph.push_back(result[i]);
623 if (endsWith(paragraph, "::"))
626 if (paragraph.length() == 2)
630 if (paragraph[paragraph.length() - 3] == ' ')
632 paragraph.resize(paragraph.length() - 3);
636 paragraph.resize(paragraph.length() - 1);
645 wrapper->wrap(std::string(breakSize, '\n'));
647 wrapper->settings().setFirstLineIndent(baseFirstLineIndent + firstIndent);
648 wrapper->settings().setIndent(baseIndent + indent);
649 wrapper->wrap(paragraph);
650 wrapper->settings().setFirstLineIndent(baseFirstLineIndent);
651 wrapper->settings().setIndent(baseIndent);
655 case eHelpOutputFormat_Rst:
657 result = repall(result, sandrRst);
658 result = replaceLinks(result);
659 result = replaceAll(result, "[REF]", "");
660 result = replaceAll(result, "[ref]", "");
661 result = removeExtraNewlinesRst(result);
662 wrapper->wrap(result);
666 GMX_THROW(InternalError("Invalid help output format"));
670 /********************************************************************
674 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
675 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL))))
679 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
680 const HelpLinks *links)
681 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links))))
685 GMX_RELEASE_ASSERT(links->impl_->format_ == format,
686 "Links must have the same output format as the context");
690 HelpWriterContext::HelpWriterContext(Impl *impl)
695 HelpWriterContext::HelpWriterContext(const HelpWriterContext &other)
696 : impl_(new Impl(*other.impl_))
700 HelpWriterContext::~HelpWriterContext()
704 void HelpWriterContext::setReplacement(const std::string &search,
705 const std::string &replace)
707 impl_->addReplacement(search, replace);
710 HelpOutputFormat HelpWriterContext::outputFormat() const
712 return impl_->state_->format_;
715 File &HelpWriterContext::outputFile() const
717 return impl_->state_->file_;
721 HelpWriterContext::substituteMarkupAndWrapToString(
722 const TextLineWrapperSettings &settings, const std::string &text) const
724 WrapperToString wrapper(settings);
725 impl_->processMarkup(text, &wrapper);
726 return wrapper.result();
729 std::vector<std::string>
730 HelpWriterContext::substituteMarkupAndWrapToVector(
731 const TextLineWrapperSettings &settings, const std::string &text) const
733 WrapperToVector wrapper(settings);
734 impl_->processMarkup(text, &wrapper);
735 return wrapper.result();
738 void HelpWriterContext::writeTitle(const std::string &title) const
740 File &file = outputFile();
741 switch (outputFormat())
743 case eHelpOutputFormat_Console:
744 file.writeLine(toUpperCase(title));
747 case eHelpOutputFormat_Rst:
748 file.writeLine(title);
749 file.writeLine(std::string(title.length(), '-'));
752 GMX_THROW(NotImplementedError(
753 "This output format is not implemented"));
757 void HelpWriterContext::writeTextBlock(const std::string &text) const
759 TextLineWrapperSettings settings;
760 if (outputFormat() == eHelpOutputFormat_Console)
762 settings.setLineLength(78);
764 outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
767 void HelpWriterContext::writeOptionListStart() const
771 void HelpWriterContext::writeOptionItem(const std::string &name,
772 const std::string &args,
773 const std::string &description) const
775 File &file = outputFile();
776 switch (outputFormat())
778 case eHelpOutputFormat_Console:
779 // TODO: Generalize this when there is need for it; the current,
780 // special implementation is in CommandLineHelpWriter.
781 GMX_THROW(NotImplementedError("Option item formatting for console output not implemented"));
783 case eHelpOutputFormat_Rst:
785 file.writeLine(formatString("``%s`` %s", name.c_str(), args.c_str()));
786 TextLineWrapperSettings settings;
787 settings.setIndent(4);
788 file.writeLine(substituteMarkupAndWrapToString(settings, description));
792 GMX_THROW(NotImplementedError(
793 "This output format is not implemented"));
797 void HelpWriterContext::writeOptionListEnd() const