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/onlinehelp/helpformat.h"
55 #include "gromacs/utility/exceptions.h"
56 #include "gromacs/utility/file.h"
57 #include "gromacs/utility/gmxassert.h"
58 #include "gromacs/utility/programcontext.h"
59 #include "gromacs/utility/stringutil.h"
61 #include "rstparser.h"
69 //! \internal \addtogroup module_onlinehelp
72 //! Characters used for reStructuredText title underlining.
73 const char g_titleChars[] = "=-^*~+#'_.";
81 /* The order of these arrays is significant. Text search and replace
82 * for each element occurs in order, so earlier changes can induce
83 * subsequent changes even though the original text might not appear
84 * to invoke the latter changes.
85 * TODO: Get rid of this behavior. It makes it very difficult to manage
86 * replacements coming from multiple sources (e.g., hyperlinks).*/
88 //! List of replacements for console output.
89 const t_sandr sandrTty[] = {
102 { "[CHEVRON]", "<" },
103 { "[chevron]", ">" },
106 { "[INT]", "integral" },
107 { "[FROM]", " from " },
116 { "[SQRT]", "sqrt(" },
130 { "[COSH]", "cosh(" },
132 { "[SINH]", "sinh(" },
134 { "[TANH]", "tanh(" },
141 //! List of replacements for reStructuredText output.
142 const t_sandr sandrRst[] = {
151 { "[CHEVRON]", "<" },
152 { "[chevron]", ">" },
155 { "[INT]", "integral" },
156 { "[FROM]", " from " },
165 { "[SQRT]", "sqrt(" },
179 { "[COSH]", "cosh(" },
181 { "[SINH]", "sinh(" },
183 { "[TANH]", "tanh(" },
191 * Replaces all entries from a list of replacements.
193 std::string repall(const std::string &s, int nsr, const t_sandr sa[])
195 std::string result(s);
196 for (int i = 0; i < nsr; ++i)
198 result = replaceAll(result, sa[i].search, sa[i].replace);
204 * Replaces all entries from a list of replacements.
206 template <size_t nsr>
207 std::string repall(const std::string &s, const t_sandr (&sa)[nsr])
209 return repall(s, nsr, sa);
213 * Custom output interface for HelpWriterContext::Impl::processMarkup().
215 * Provides an interface that is used to implement different types of output
216 * from HelpWriterContext::Impl::processMarkup().
218 class WrapperInterface
221 virtual ~WrapperInterface() {}
224 * Provides the wrapping settings.
226 * HelpWriterContext::Impl::processMarkup() may provide some default
227 * values for the settings if they are not set; this is the reason the
228 * return value is not const.
230 virtual TextLineWrapperSettings &settings() = 0;
231 //! Appends the given string to output.
232 virtual void wrap(const std::string &text) = 0;
236 * Wraps markup output into a single string.
238 class WrapperToString : public WrapperInterface
241 //! Creates a wrapper with the given settings.
242 explicit WrapperToString(const TextLineWrapperSettings &settings)
247 virtual TextLineWrapperSettings &settings()
249 return wrapper_.settings();
251 virtual void wrap(const std::string &text)
253 result_.append(wrapper_.wrapToString(text));
255 //! Returns the result string.
256 const std::string &result() const { return result_; }
259 TextLineWrapper wrapper_;
264 * Wraps markup output into a vector of string (one line per element).
266 class WrapperToVector : public WrapperInterface
269 //! Creates a wrapper with the given settings.
270 explicit WrapperToVector(const TextLineWrapperSettings &settings)
275 virtual TextLineWrapperSettings &settings()
277 return wrapper_.settings();
279 virtual void wrap(const std::string &text)
281 const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
282 result_.insert(result_.end(), lines.begin(), lines.end());
284 //! Returns a vector with the output lines.
285 const std::vector<std::string> &result() const { return result_; }
288 TextLineWrapper wrapper_;
289 std::vector<std::string> result_;
293 * Makes the string uppercase.
295 * \param[in] text Input text.
296 * \returns \p text with all characters transformed to uppercase.
297 * \throws std::bad_alloc if out of memory.
299 std::string toUpperCase(const std::string &text)
301 std::string result(text);
302 std::transform(result.begin(), result.end(), result.begin(), toupper);
307 * Removes extra newlines from reStructuredText.
309 * \param[in] text Input text.
310 * \returns \p text with all sequences of more than two newlines replaced
311 * with just two newlines.
312 * \throws std::bad_alloc if out of memory.
314 std::string removeExtraNewlinesRst(const std::string &text)
316 // Start from 2, so that all newlines in the beginning get stripped off.
317 int newlineCount = 2;
319 result.reserve(text.length());
320 for (size_t i = 0; i < text.length(); ++i)
325 if (newlineCount > 2)
334 result.push_back(text[i]);
336 size_t last = result.find_last_not_of('\n');
337 if (last != std::string::npos)
339 result.resize(last + 1);
348 /********************************************************************
353 * Private implementation class for HelpLinks.
355 * \ingroup module_onlinehelp
357 class HelpLinks::Impl
362 LinkItem(const std::string &linkName,
363 const std::string &replacement)
364 : linkName(linkName), replacement(replacement)
367 std::string linkName;
368 std::string replacement;
371 //! Shorthand for a list of links.
372 typedef std::vector<LinkItem> LinkList;
374 //! Initializes empty links with the given format.
375 explicit Impl(HelpOutputFormat format) : format_(format)
381 //! Output format for which the links are formatted.
382 HelpOutputFormat format_;
385 /********************************************************************
389 HelpLinks::HelpLinks(HelpOutputFormat format) : impl_(new Impl(format))
393 HelpLinks::~HelpLinks()
397 void HelpLinks::addLink(const std::string &linkName,
398 const std::string &targetName,
399 const std::string &displayName)
401 std::string replacement;
402 switch (impl_->format_)
404 case eHelpOutputFormat_Console:
405 replacement = repall(displayName, sandrTty);
407 case eHelpOutputFormat_Rst:
408 replacement = targetName;
411 GMX_RELEASE_ASSERT(false, "Output format not implemented for links");
413 impl_->links_.push_back(Impl::LinkItem(linkName, replacement));
416 /********************************************************************
417 * HelpWriterContext::Impl
421 * Private implementation class for HelpWriterContext.
423 * \ingroup module_onlinehelp
425 class HelpWriterContext::Impl
429 * Shared, non-modifiable state for context objects.
431 * Contents of this structure are shared between all context objects
432 * that are created from a common parent.
433 * This state should not be modified after construction.
435 * \ingroup module_onlinehelp
440 //! Initializes the state with the given parameters.
441 SharedState(File *file, HelpOutputFormat format,
442 const HelpLinks *links)
443 : file_(*file), format_(format), links_(links)
448 * Returns a formatter for formatting options lists for console
451 * The formatter is lazily initialized on first access.
453 TextTableFormatter &consoleOptionsFormatter() const
455 GMX_RELEASE_ASSERT(format_ == eHelpOutputFormat_Console,
456 "Accessing console formatter for non-console output");
457 if (!consoleOptionsFormatter_)
459 consoleOptionsFormatter_.reset(new TextTableFormatter());
460 consoleOptionsFormatter_->setFirstColumnIndent(1);
461 consoleOptionsFormatter_->addColumn(NULL, 7, false);
462 consoleOptionsFormatter_->addColumn(NULL, 18, false);
463 consoleOptionsFormatter_->addColumn(NULL, 16, false);
464 consoleOptionsFormatter_->addColumn(NULL, 28, false);
466 return *consoleOptionsFormatter_;
469 //! Output file to which the help is written.
471 //! Output format for the help output.
472 HelpOutputFormat format_;
474 const HelpLinks *links_;
477 //! Formatter for console output options.
478 mutable boost::scoped_ptr<TextTableFormatter> consoleOptionsFormatter_;
483 ReplaceItem(const std::string &search,
484 const std::string &replace)
485 : search(search), replace(replace)
492 //! Smart pointer type for managing the shared state.
493 typedef boost::shared_ptr<const SharedState> StatePointer;
494 //! Shorthand for a list of markup/other replacements.
495 typedef std::vector<ReplaceItem> ReplaceList;
497 //! Initializes the context with the given state and section depth.
498 Impl(const StatePointer &state, int sectionDepth)
499 : state_(state), sectionDepth_(sectionDepth)
503 //! Adds a new replacement.
504 void addReplacement(const std::string &search,
505 const std::string &replace)
507 replacements_.push_back(ReplaceItem(search, replace));
510 //! Replaces links in a given string.
511 std::string replaceLinks(const std::string &input) const;
514 * Process markup and wrap lines within a block of text.
516 * \param[in] text Text to process.
517 * \param wrapper Object used to wrap the text.
519 * The \p wrapper should take care of either writing the text to output
520 * or providing an interface for the caller to retrieve the output.
522 void processMarkup(const std::string &text,
523 WrapperInterface *wrapper) const;
525 //! Constant state shared by all child context objects.
527 //! List of markup/other replacements.
528 ReplaceList replacements_;
529 //! Number of subsections above this context.
533 GMX_DISALLOW_ASSIGN(Impl);
536 std::string HelpWriterContext::Impl::replaceLinks(const std::string &input) const
538 std::string result(input);
539 if (state_->links_ != NULL)
541 HelpLinks::Impl::LinkList::const_iterator link;
542 for (link = state_->links_->impl_->links_.begin();
543 link != state_->links_->impl_->links_.end(); ++link)
545 result = replaceAllWords(result, link->linkName, link->replacement);
551 void HelpWriterContext::Impl::processMarkup(const std::string &text,
552 WrapperInterface *wrapper) const
554 std::string result(text);
555 for (ReplaceList::const_iterator i = replacements_.begin();
556 i != replacements_.end(); ++i)
558 result = replaceAll(result, i->search, i->replace);
560 switch (state_->format_)
562 case eHelpOutputFormat_Console:
564 const int baseFirstLineIndent = wrapper->settings().firstLineIndent();
565 const int baseIndent = wrapper->settings().indent();
566 result = repall(result, sandrTty);
567 result = replaceLinks(result);
568 std::string paragraph;
569 paragraph.reserve(result.length());
570 RstParagraphIterator iter(result);
571 while (iter.nextParagraph())
573 iter.getParagraphText(¶graph);
574 wrapper->settings().setFirstLineIndent(baseFirstLineIndent + iter.firstLineIndent());
575 wrapper->settings().setIndent(baseIndent + iter.indent());
576 wrapper->wrap(paragraph);
578 wrapper->settings().setFirstLineIndent(baseFirstLineIndent);
579 wrapper->settings().setIndent(baseIndent);
582 case eHelpOutputFormat_Rst:
584 result = repall(result, sandrRst);
585 result = replaceLinks(result);
586 result = replaceAll(result, "[REF]", "");
587 result = replaceAll(result, "[ref]", "");
588 result = removeExtraNewlinesRst(result);
589 wrapper->wrap(result);
593 GMX_THROW(InternalError("Invalid help output format"));
597 /********************************************************************
601 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format)
602 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, NULL)), 0))
606 HelpWriterContext::HelpWriterContext(File *file, HelpOutputFormat format,
607 const HelpLinks *links)
608 : impl_(new Impl(Impl::StatePointer(new Impl::SharedState(file, format, links)), 0))
612 GMX_RELEASE_ASSERT(links->impl_->format_ == format,
613 "Links must have the same output format as the context");
617 HelpWriterContext::HelpWriterContext(Impl *impl)
622 HelpWriterContext::HelpWriterContext(const HelpWriterContext &other)
623 : impl_(new Impl(*other.impl_))
627 HelpWriterContext::~HelpWriterContext()
631 void HelpWriterContext::setReplacement(const std::string &search,
632 const std::string &replace)
634 impl_->addReplacement(search, replace);
637 HelpOutputFormat HelpWriterContext::outputFormat() const
639 return impl_->state_->format_;
642 File &HelpWriterContext::outputFile() const
644 return impl_->state_->file_;
647 void HelpWriterContext::enterSubSection(const std::string &title)
649 GMX_RELEASE_ASSERT(impl_->sectionDepth_ - 1 < static_cast<int>(std::strlen(g_titleChars)),
650 "Too deeply nested subsections");
652 ++impl_->sectionDepth_;
656 HelpWriterContext::substituteMarkupAndWrapToString(
657 const TextLineWrapperSettings &settings, const std::string &text) const
659 WrapperToString wrapper(settings);
660 impl_->processMarkup(text, &wrapper);
661 return wrapper.result();
664 std::vector<std::string>
665 HelpWriterContext::substituteMarkupAndWrapToVector(
666 const TextLineWrapperSettings &settings, const std::string &text) const
668 WrapperToVector wrapper(settings);
669 impl_->processMarkup(text, &wrapper);
670 return wrapper.result();
673 void HelpWriterContext::writeTitle(const std::string &title) const
679 File &file = outputFile();
680 switch (outputFormat())
682 case eHelpOutputFormat_Console:
683 file.writeLine(toUpperCase(title));
686 case eHelpOutputFormat_Rst:
687 file.writeLine(title);
688 file.writeLine(std::string(title.length(),
689 g_titleChars[impl_->sectionDepth_]));
692 GMX_THROW(NotImplementedError(
693 "This output format is not implemented"));
697 void HelpWriterContext::writeTextBlock(const std::string &text) const
699 TextLineWrapperSettings settings;
700 if (outputFormat() == eHelpOutputFormat_Console)
702 settings.setLineLength(78);
704 outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
707 void HelpWriterContext::writeOptionListStart() const
711 void HelpWriterContext::writeOptionItem(const std::string &name,
712 const std::string &value,
713 const std::string &defaultValue,
714 const std::string &info,
715 const std::string &description) const
717 File &file = outputFile();
718 switch (outputFormat())
720 case eHelpOutputFormat_Console:
722 TextTableFormatter &formatter(impl_->state_->consoleOptionsFormatter());
724 formatter.addColumnLine(0, name);
725 formatter.addColumnLine(1, value);
726 if (!defaultValue.empty())
728 formatter.addColumnLine(2, "(" + defaultValue + ")");
732 formatter.addColumnLine(3, "(" + info + ")");
734 TextLineWrapperSettings settings;
735 settings.setIndent(11);
736 settings.setLineLength(78);
737 std::string formattedDescription
738 = substituteMarkupAndWrapToString(settings, description);
739 file.writeLine(formatter.formatRow());
740 file.writeLine(formattedDescription);
743 case eHelpOutputFormat_Rst:
745 std::string args(value);
746 if (!defaultValue.empty())
749 args.append(defaultValue);
758 file.writeLine(formatString("``%s`` %s", name.c_str(), args.c_str()));
759 TextLineWrapperSettings settings;
760 settings.setIndent(4);
761 file.writeLine(substituteMarkupAndWrapToString(settings, description));
765 GMX_THROW(NotImplementedError(
766 "This output format is not implemented"));
770 void HelpWriterContext::writeOptionListEnd() const