Improve markup substitution in HelpWriterContext.
authorTeemu Murtola <teemu.murtola@gmail.com>
Thu, 30 Aug 2012 03:48:19 +0000 (06:48 +0300)
committerTeemu Murtola <teemu.murtola@gmail.com>
Mon, 7 Oct 2013 15:07:02 +0000 (18:07 +0300)
- substituteMarkup() replaced with an interface that also does line
  wrapping at the same time.  Allows more complex line wrapping that
  can also depend on the input markup, and clarifies responsibilities in
  the code.
- Make TextTableFormatter use the new interface, removing explicit
  substituteMarkup() calls from elsewhere in the code.
- Structure the substitution to allow easy addition of more output
  formats.

Related to #685 and #969.

Change-Id: Id34be9489aa3a90d94cd87f8936b030bc21f1c98

src/gromacs/commandline/cmdlinehelpwriter.cpp
src/gromacs/onlinehelp/helpformat.cpp
src/gromacs/onlinehelp/helpformat.h
src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/onlinehelp/helpwritercontext.h

index 029aef5b44cbfe3e5bede231f3935ddeb8cde47b..e65a1ac39534fdae15164a42bfe9317cd37222bc 100644 (file)
@@ -357,7 +357,7 @@ void OptionsConsoleFormatter::formatFileOption(
     }
     bool bLongType = (type.length() > 12U);
     fileOptionFormatter_.addColumnLine(2, type);
-    fileOptionFormatter_.addColumnLine(3, context.substituteMarkup(option.description()));
+    fileOptionFormatter_.addColumnHelpTextBlock(3, context, option.description());
 
     // Compute layout.
     if (name.length() > 6U || firstShortValue > 0)
@@ -413,7 +413,7 @@ void OptionsConsoleFormatter::formatOption(
     }
     genericOptionFormatter_.addColumnLine(2, values);
 
-    std::string             description(context.substituteMarkup(option.description()));
+    std::string             description(option.description());
     const DoubleOptionInfo *doubleOption = option.toType<DoubleOptionInfo>();
     if (doubleOption != NULL && doubleOption->isTime())
     {
@@ -435,7 +435,7 @@ void OptionsConsoleFormatter::formatOption(
             description.append(allowedValues[i]);
         }
     }
-    genericOptionFormatter_.addColumnLine(3, description);
+    genericOptionFormatter_.addColumnHelpTextBlock(3, context, description);
     if (values.length() > 6U)
     {
         genericOptionFormatter_.setColumnFirstLineOffset(3, 1);
@@ -452,7 +452,8 @@ void OptionsConsoleFormatter::formatSelectionOption(
     selectionOptionFormatter_.clear();
     std::string name(formatString("-%s", option.name().c_str()));
     selectionOptionFormatter_.addColumnLine(0, name);
-    selectionOptionFormatter_.addColumnLine(1, context.substituteMarkup(option.description()));
+    selectionOptionFormatter_.addColumnHelpTextBlock(1, context,
+                                                     option.description());
     file.writeString(selectionOptionFormatter_.formatRow());
 
     TextLineWrapper wrapper;
index 7e8c22ac9ddf7b781d02e6a70a426a7f14ad7294..17e66c93530ba52051e2aa86390b0706005c8ba4 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013, by the GROMACS development team, led by
  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
  * others, as listed in the AUTHORS file in the top-level source
  * directory and at http://www.gromacs.org.
@@ -45,6 +45,7 @@
 #include <string>
 #include <vector>
 
+#include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
 
@@ -221,6 +222,20 @@ void TextTableFormatter::addColumnLine(int index, const std::string &text)
     column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
 }
 
+void TextTableFormatter::addColumnHelpTextBlock(
+        int index, const HelpWriterContext &context, const std::string &text)
+{
+    Impl::ColumnData       &column = impl_->columnData(index);
+    TextLineWrapperSettings settings;
+    if (column.bWrap_)
+    {
+        settings.setLineLength(column.width());
+    }
+    std::vector<std::string> lines(
+            context.substituteMarkupAndWrapToVector(settings, text));
+    column.lines_.insert(column.lines_.end(), lines.begin(), lines.end());
+}
+
 void TextTableFormatter::setColumnFirstLineOffset(int index, int firstLine)
 {
     GMX_ASSERT(firstLine >= 0, "Invalid first line");
index 98cea4d3852efc5b6de6ce8f3a6d2307a0a788bc..a89cab9f6a332a23c96ffbe3537988299c976919 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013, by the GROMACS development team, led by
  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
  * others, as listed in the AUTHORS file in the top-level source
  * directory and at http://www.gromacs.org.
@@ -51,6 +51,7 @@ namespace gmx
 {
 
 class File;
+class HelpWriterContext;
 
 /*! \libinternal \brief
  * Formats rows of a table for text output.
@@ -164,6 +165,19 @@ class TextTableFormatter
          * fits the column.
          */
         void addColumnLine(int index, const std::string &text);
+        /*! \brief
+         * Adds text containing help markup to be printed in a column.
+         *
+         * \param[in]  index     Zero-based column index.
+         * \param[in]  context   Context to use for markup processing.
+         * \param[in]  text      Text to add.
+         *
+         * Works as addColumnLine(), except that it uses
+         * HelpWriterContext::substituteMarkupAndWrapToVector() to process
+         * markup in the input text instead of just wrapping it as plain text.
+         */
+        void addColumnHelpTextBlock(int index, const HelpWriterContext &context,
+                                    const std::string &text);
         /*! \brief
          * Sets the first line to which text is printed for a column.
          *
index 94dfbb150a56b8854dd04efd2d5383aadb82d32f..0dc471cb9f63f95b014b579de425998af2acdd27 100644 (file)
@@ -45,6 +45,7 @@
 
 #include <algorithm>
 #include <string>
+#include <vector>
 
 #include "gromacs/legacyheaders/smalloc.h"
 
 #include "gromacs/utility/programinfo.h"
 #include "gromacs/utility/stringutil.h"
 
+namespace gmx
+{
+
 namespace
 {
 
+/*! \internal \brief
+ * Custom output interface for HelpWriterContext::Impl::processMarkup().
+ *
+ * Provides an interface that is used to implement different types of output
+ * from HelpWriterContext::Impl::processMarkup().
+ *
+ * \ingroup module_onlinehelp
+ */
+class WrapperInterface
+{
+    public:
+        virtual ~WrapperInterface() {}
+
+        /*! \brief
+         * Provides the wrapping settings.
+         *
+         * HelpWriterContext::Impl::processMarkup() may provide some default
+         * values for the settings if they are not set; this is the reason the
+         * return value is not const.
+         */
+        virtual TextLineWrapperSettings &settings() = 0;
+        //! Appends the given string to output.
+        virtual void wrap(const std::string &text)  = 0;
+};
+
+/*! \internal \brief
+ * Wraps markup output into a single string.
+ *
+ * \ingroup module_onlinehelp
+ */
+class WrapperToString : public WrapperInterface
+{
+    public:
+        //! Creates a wrapper with the given settings.
+        explicit WrapperToString(const TextLineWrapperSettings &settings)
+            : wrapper_(settings)
+        {
+        }
+
+        virtual TextLineWrapperSettings &settings()
+        {
+            return wrapper_.settings();
+        }
+        virtual void wrap(const std::string &text)
+        {
+            result_.append(wrapper_.wrapToString(text));
+        }
+        //! Returns the result string.
+        const std::string &result() const { return result_; }
+
+    private:
+        TextLineWrapper         wrapper_;
+        std::string             result_;
+};
+
+/*! \internal \brief
+ * Wraps markup output into a vector of string (one line per element).
+ *
+ * \ingroup module_onlinehelp
+ */
+class WrapperToVector : public WrapperInterface
+{
+    public:
+        //! Creates a wrapper with the given settings.
+        explicit WrapperToVector(const TextLineWrapperSettings &settings)
+            : wrapper_(settings)
+        {
+        }
+
+        virtual TextLineWrapperSettings &settings()
+        {
+            return wrapper_.settings();
+        }
+        virtual void wrap(const std::string &text)
+        {
+            const std::vector<std::string> &lines = wrapper_.wrapToVector(text);
+            result_.insert(result_.end(), lines.begin(), lines.end());
+        }
+        //! Returns a vector with the output lines.
+        const std::vector<std::string> &result() const { return result_; }
+
+    private:
+        TextLineWrapper          wrapper_;
+        std::vector<std::string> result_;
+};
+
 /*! \internal \brief
  * Make the string uppercase.
  *
@@ -73,10 +163,7 @@ std::string toUpperCase(const std::string &text)
     return result;
 }
 
-} // namespace
-
-namespace gmx
-{
+}   // namespace
 
 /********************************************************************
  * HelpWriterContext::Impl
@@ -96,12 +183,50 @@ class HelpWriterContext::Impl
         {
         }
 
+        /*! \brief
+         * Process markup and wrap lines within a block of text.
+         *
+         * \param[in] text     Text to process.
+         * \param     wrapper  Object used to wrap the text.
+         *
+         * The \p wrapper should take care of either writing the text to output
+         * or providing an interface for the caller to retrieve the output.
+         */
+        void processMarkup(const std::string &text,
+                           WrapperInterface  *wrapper) const;
+
         //! Output file to which the help is written.
         File                   &file_;
         //! Output format for the help output.
         HelpOutputFormat        format_;
 };
 
+void HelpWriterContext::Impl::processMarkup(const std::string &text,
+                                            WrapperInterface  *wrapper) const
+{
+    const char *program = ProgramInfo::getInstance().programName().c_str();
+    std::string result(text);
+    result = replaceAll(result, "[PROGRAM]", program);
+    switch (format_)
+    {
+        case eHelpOutputFormat_Console:
+        {
+            {
+                char            *resultStr = check_tty(result.c_str());
+                scoped_ptr_sfree resultGuard(resultStr);
+                result = resultStr;
+            }
+            if (wrapper->settings().lineLength() == 0)
+            {
+                wrapper->settings().setLineLength(78);
+            }
+            return wrapper->wrap(result);
+        }
+        default:
+            GMX_THROW(InternalError("Invalid help output format"));
+    }
+}
+
 /********************************************************************
  * HelpWriterContext
  */
@@ -125,18 +250,22 @@ File &HelpWriterContext::outputFile() const
     return impl_->file_;
 }
 
-std::string HelpWriterContext::substituteMarkup(const std::string &text) const
+std::string
+HelpWriterContext::substituteMarkupAndWrapToString(
+        const TextLineWrapperSettings &settings, const std::string &text) const
 {
-    if (outputFormat() != eHelpOutputFormat_Console)
-    {
-        // TODO: Implement once the situation with Redmine issue #969 is more
-        // clear.
-        GMX_THROW(NotImplementedError(
-                          "This output format is not implemented"));
-    }
-    char            *resultStr = check_tty(text.c_str());
-    scoped_ptr_sfree resultGuard(resultStr);
-    return std::string(resultStr);
+    WrapperToString wrapper(settings);
+    impl_->processMarkup(text, &wrapper);
+    return wrapper.result();
+}
+
+std::vector<std::string>
+HelpWriterContext::substituteMarkupAndWrapToVector(
+        const TextLineWrapperSettings &settings, const std::string &text) const
+{
+    WrapperToVector wrapper(settings);
+    impl_->processMarkup(text, &wrapper);
+    return wrapper.result();
 }
 
 void HelpWriterContext::writeTitle(const std::string &title) const
@@ -155,18 +284,13 @@ void HelpWriterContext::writeTitle(const std::string &title) const
 
 void HelpWriterContext::writeTextBlock(const std::string &text) const
 {
-    if (outputFormat() != eHelpOutputFormat_Console)
-    {
-        // TODO: Implement once the situation with Redmine issue #969 is more
-        // clear.
-        GMX_THROW(NotImplementedError(
-                          "This output format is not implemented"));
-    }
-    TextLineWrapper wrapper;
-    wrapper.settings().setLineLength(78);
-    const char     *program = ProgramInfo::getInstance().programName().c_str();
-    std::string     newText = replaceAll(text, "[PROGRAM]", program);
-    outputFile().writeLine(wrapper.wrapToString(substituteMarkup(newText)));
+    writeTextBlock(TextLineWrapperSettings(), text);
+}
+
+void HelpWriterContext::writeTextBlock(const TextLineWrapperSettings &settings,
+                                       const std::string             &text) const
+{
+    outputFile().writeLine(substituteMarkupAndWrapToString(settings, text));
 }
 
 } // namespace gmx
index 3b09280793ca51d0db71a18f74a351d08ef0ea39..d2ea44daef7417ff28462a5c81248182ad661bef 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2012, by the GROMACS development team, led by
+ * Copyright (c) 2012,2013, by the GROMACS development team, led by
  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
  * others, as listed in the AUTHORS file in the top-level source
  * directory and at http://www.gromacs.org.
@@ -44,6 +44,7 @@
 #define GMX_ONLINEHELP_HELPWRITERCONTEXT_H
 
 #include <string>
+#include <vector>
 
 #include "../utility/common.h"
 
@@ -51,6 +52,7 @@ namespace gmx
 {
 
 class File;
+class TextLineWrapperSettings;
 
 /*! \cond libapi */
 //! \libinternal Output format for help writing.
@@ -107,13 +109,32 @@ class HelpWriterContext
         File &outputFile() const;
 
         /*! \brief
-         * Substitutes markup used in help text.
+         * Substitutes markup used in help text and wraps lines.
          *
-         * \param[in] text  Text to substitute.
-         * \returns   \p text with markup substituted.
+         * \param[in] settings Line wrapper settings.
+         * \param[in] text     Text to substitute.
+         * \returns   \p text with markup substituted and wrapped.
          * \throws    std::bad_alloc if out of memory.
+         *
+         * \see TextLineWrapper::wrapToString()
          */
-        std::string substituteMarkup(const std::string &text) const;
+        std::string
+        substituteMarkupAndWrapToString(const TextLineWrapperSettings &settings,
+                                        const std::string             &text) const;
+        /*! \brief
+         * Substitutes markup used in help text and wraps lines.
+         *
+         * \param[in] settings Line wrapper settings.
+         * \param[in] text     Text to substitute.
+         * \returns   \p text with markup substituted and wrapped as a list of
+         *      lines.
+         * \throws    std::bad_alloc if out of memory.
+         *
+         * \see TextLineWrapper::wrapToVector()
+         */
+        std::vector<std::string>
+        substituteMarkupAndWrapToVector(const TextLineWrapperSettings &settings,
+                                        const std::string             &text) const;
         /*! \brief
          * Writes a title for the current help topic.
          *
@@ -129,13 +150,23 @@ class HelpWriterContext
          * \throws    std::bad_alloc if out of memory.
          * \throws    FileIOError on any I/O error.
          *
-         * In addition to substituteMarkup(), also does line wrapping for
-         * console output.
-         *
-         * TODO: This function and substituteMarkup() should work more
-         * similarly.
+         * Convenience function that calls substituteMarkupAndWrapToString()
+         * and writes the result directly to the output file.
          */
         void writeTextBlock(const std::string &text) const;
+        /*! \brief
+         * Writes a formatted text block into the output.
+         *
+         * \param[in] settings Line wrapper settings.
+         * \param[in] text     Text to format.
+         * \throws    std::bad_alloc if out of memory.
+         * \throws    FileIOError on any I/O error.
+         *
+         * Convenience function that calls substituteMarkupAndWrapToString()
+         * and writes the result directly to the output file.
+         */
+        void writeTextBlock(const TextLineWrapperSettings &settings,
+                            const std::string             &text) const;
 
     private:
         class Impl;