Improve lists in user guide for selections
authorTeemu Murtola <teemu.murtola@gmail.com>
Sun, 24 May 2015 03:33:08 +0000 (06:33 +0300)
committerMark Abraham <mark.j.abraham@gmail.com>
Thu, 28 May 2015 12:25:24 +0000 (14:25 +0200)
- Support enumerated list formatting in console output to make it
  possible to use reStructuredText enumerated lists in help texts.
- Make the keyword list use a bullet list for the keyword groups,
  providing some visual structure for the HTML output.  The extra stars
  do not hurt in the console output, either, but changed the extra help
  indicator to be a + instead of * to reduce confusion with these other
  stars.

Change-Id: I4d5063d4f117a31ea31d8d78cd799dfbd0f1eac8

src/gromacs/onlinehelp/helpwritercontext.cpp
src/gromacs/onlinehelp/tests/helpwritercontext.cpp
src/gromacs/onlinehelp/tests/refdata/HelpWriterContextTest_FormatsBulletList.xml
src/gromacs/onlinehelp/tests/refdata/HelpWriterContextTest_FormatsEnumeratedList.xml [new file with mode: 0644]
src/gromacs/selection/selhelp.cpp

index 8d5a44903be1664c72c3b4578c3a5f5107dab949..415f2602f7f9d57d4cae5d5233d6a3fc058b38f0 100644 (file)
@@ -309,8 +309,7 @@ std::string toUpperCase(const std::string &text)
  * \param[in] text  Input text.
  * \returns   \p text with all sequences of more than two newlines replaced
  *     with just two newlines.
- *
- * Does not throw.
+ * \throws    std::bad_alloc if out of memory.
  */
 std::string removeExtraNewlinesRst(const std::string &text)
 {
@@ -342,6 +341,36 @@ std::string removeExtraNewlinesRst(const std::string &text)
     return result;
 }
 
+/*! \brief
+ * Returns `true` if a list item starts in \p text at \p index.
+ *
+ * Does not throw.
+ */
+bool startsListItem(const std::string &text, size_t index)
+{
+    if (text.length() <= index + 1)
+    {
+        return false;
+    }
+    if (text[index] == '*' && std::isspace(text[index+1]))
+    {
+        return true;
+    }
+    if (std::isdigit(text[index]))
+    {
+        while (index < text.length() && std::isdigit(text[index]))
+        {
+            ++index;
+        }
+        if (text.length() > index + 1 && text[index] == '.'
+            && std::isspace(text[index+1]))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 //! \}
 
 }   // namespace
@@ -606,8 +635,7 @@ void HelpWriterContext::Impl::processMarkup(const std::string &text,
                             ++currentIndent;
                             continue;
                         }
-                        else if (i + 1 < result.length()
-                                 && result[i] == '*' && result[i + 1] == ' ')
+                        else if (startsListItem(result, i))
                         {
                             if (currentLine > 0)
                             {
@@ -619,7 +647,17 @@ void HelpWriterContext::Impl::processMarkup(const std::string &text,
                                 nextBreakSize = 1;
                                 break;
                             }
-                            indent = currentIndent + 2;
+                            int prefixLength = 0;
+                            while (!std::isspace(result[i + prefixLength]))
+                            {
+                                ++prefixLength;
+                            }
+                            while (i + prefixLength < result.length()
+                                   && std::isspace(result[i + prefixLength]))
+                            {
+                                ++prefixLength;
+                            }
+                            indent = currentIndent + prefixLength;
                         }
                         bLineStart = false;
                     }
index 4216fccf0ba70f3746af6348d04bdf8a8b3f4d97..9db5f0e3403159f08174d5320e5b40a99ef86637 100644 (file)
@@ -173,10 +173,31 @@ TEST_F(HelpWriterContextTest, FormatsBulletList)
     const char *const text[] = {
         "Sample list:",
         "",
-        " * first item",
-        " * second item that",
+        "* first item",
+        "* second item that",
+        "  spans multiple lines",
+        "* third item that has a single long line",
+        "",
+        "Normal paragraph"
+    };
+    // Wrapping to rst with a fixed line length does not currently work
+    // correctly, but it is not used, either.
+    settings_.setLineLength(15);
+    testFormatting(text);
+}
+
+TEST_F(HelpWriterContextTest, FormatsEnumeratedList)
+{
+    const char *const text[] = {
+        "Sample list:",
+        "",
+        "1. first item",
+        "2. second item that",
         "   spans multiple lines",
-        " * third item that has a single long line",
+        "3. third item that has a single long line",
+        "",
+        "9.  Item with extra indentation",
+        "10. Double digit item",
         "",
         "Normal paragraph"
     };
index 2bceed2aaf8a898de0bac07e38dca49e4c73a595..2acdee18802d31251e56589a1ac7dcfd98b18869 100644 (file)
@@ -4,27 +4,27 @@
   <String Name="Console"><![CDATA[
 Sample list:
 
- * first item
- * second item
-   that spans
-   multiple
-   lines
- * third item
-   that has a
-   single long
-   line
+* first item
+* second item
+  that spans
+  multiple
+  lines
+* third item
+  that has a
+  single long
+  line
 
 Normal
 paragraph]]></String>
   <String Name="reStructuredText"><![CDATA[
 Sample list:
 
- * first item
- * second item
+* first item
+* second item
 that
-   spans
+  spans
 multiple lines
- * third item
+* third item
 that has a
 single long
 line
diff --git a/src/gromacs/onlinehelp/tests/refdata/HelpWriterContextTest_FormatsEnumeratedList.xml b/src/gromacs/onlinehelp/tests/refdata/HelpWriterContextTest_FormatsEnumeratedList.xml
new file mode 100644 (file)
index 0000000..b68ed95
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <String Name="Console"><![CDATA[
+Sample list:
+
+1. first item
+2. second item
+   that spans
+   multiple
+   lines
+3. third item
+   that has a
+   single long
+   line
+
+9.  Item with
+    extra
+    indentation
+10. Double
+    digit item
+
+Normal
+paragraph]]></String>
+  <String Name="reStructuredText"><![CDATA[
+Sample list:
+
+1. first item
+2. second item
+that
+   spans
+multiple lines
+3. third item
+that has a
+single long
+line
+
+9.  Item with
+extra
+indentation
+10. Double
+digit item
+
+Normal
+paragraph]]></String>
+</ReferenceData>
index 746b4394eff038867ab5577a367f4a53c7d5432b..891e927509aaca89e6252e7d8a7190d51eb43e02 100644 (file)
@@ -316,7 +316,7 @@ const char        KeywordsHelpText::title[] =
     "Selection keywords";
 const char *const KeywordsHelpText::text[] = {
     "The following selection keywords are currently available.",
-    "For keywords marked with a star, additional help is available through",
+    "For keywords marked with a plus, additional help is available through",
     "a subtopic KEYWORD, where KEYWORD is the name of the keyword.",
 };
 
@@ -389,39 +389,39 @@ const char        PositionsHelpText::name[]  = "positions";
 const char        PositionsHelpText::title[] =
     "Specifying positions in selections";
 const char *const PositionsHelpText::text[] = {
-    "Possible ways of specifying positions in selections are:[PAR]",
-
+    "Possible ways of specifying positions in selections are:",
+    "",
     "1. A constant position can be defined as [TT][XX, YY, ZZ][tt], where",
-    "[TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
-
+    "   [TT]XX[tt], [TT]YY[tt] and [TT]ZZ[tt] are real numbers.[PAR]",
+    "",
     "2. [TT]com of ATOM_EXPR [pbc][tt] or [TT]cog of ATOM_EXPR [pbc][tt]",
-    "calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
-    "[TT]pbc[tt] is specified, the center is calculated iteratively to try",
-    "to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
-    "boundary conditions.[PAR]",
-
+    "   calculate the center of mass/geometry of [TT]ATOM_EXPR[tt]. If",
+    "   [TT]pbc[tt] is specified, the center is calculated iteratively to try",
+    "   to deal with cases where [TT]ATOM_EXPR[tt] wraps around periodic",
+    "   boundary conditions.",
+    "",
     "3. [TT]POSTYPE of ATOM_EXPR[tt] calculates the specified positions for",
-    "the atoms in [TT]ATOM_EXPR[tt].",
-    "[TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
-    "[TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
-    "[TT]part_[tt] or [TT]dyn_[tt].",
-    "[TT]whole_[tt] calculates the centers for the whole residue/molecule,",
-    "even if only part of it is selected.",
-    "[TT]part_[tt] prefix calculates the centers for the selected atoms, but",
-    "uses always the same atoms for the same residue/molecule. The used atoms",
-    "are determined from the the largest group allowed by the selection.",
-    "[TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
-    "If no prefix is specified, whole selections default to [TT]part_[tt] and",
-    "other places default to [TT]whole_[tt].",
-    "The latter is often desirable to select the same molecules in different",
-    "tools, while the first is a compromise between speed ([TT]dyn_[tt]",
-    "positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
-    "behavior.[PAR]",
-
+    "   the atoms in [TT]ATOM_EXPR[tt].",
+    "   [TT]POSTYPE[tt] can be [TT]atom[tt], [TT]res_com[tt], [TT]res_cog[tt],",
+    "   [TT]mol_com[tt] or [TT]mol_cog[tt], with an optional prefix [TT]whole_[tt]",
+    "   [TT]part_[tt] or [TT]dyn_[tt].",
+    "   [TT]whole_[tt] calculates the centers for the whole residue/molecule,",
+    "   even if only part of it is selected.",
+    "   [TT]part_[tt] prefix calculates the centers for the selected atoms, but",
+    "   uses always the same atoms for the same residue/molecule. The used atoms",
+    "   are determined from the the largest group allowed by the selection.",
+    "   [TT]dyn_[tt] calculates the centers strictly only for the selected atoms.",
+    "   If no prefix is specified, whole selections default to [TT]part_[tt] and",
+    "   other places default to [TT]whole_[tt].",
+    "   The latter is often desirable to select the same molecules in different",
+    "   tools, while the first is a compromise between speed ([TT]dyn_[tt]",
+    "   positions can be slower to evaluate than [TT]part_[tt]) and intuitive",
+    "   behavior.",
+    "",
     "4. [TT]ATOM_EXPR[tt], when given for whole selections, is handled as 3.",
-    "above, using the position type from the command-line argument",
-    "[TT]-seltype[tt].[PAR]",
-
+    "   above, using the position type from the command-line argument",
+    "   [TT]-seltype[tt].",
+    "",
     "Selection keywords that select atoms based on their positions, such as",
     "[TT]dist from[tt], use by default the positions defined by the",
     "[TT]-selrpos[tt] command-line option.",
@@ -677,11 +677,13 @@ void KeywordsHelpTopic::writeHelp(const HelpWriterContext &context) const
 void KeywordsHelpTopic::writeKeywordListStart(const HelpWriterContext &context,
                                               const char              *heading) const
 {
-    context.writeTextBlock(heading);
+    std::string fullHeading("* ");
+    fullHeading.append(heading);
+    context.writeTextBlock(fullHeading);
     if (context.outputFormat() == eHelpOutputFormat_Rst)
     {
         context.writeTextBlock("");
-        context.writeTextBlock("::");
+        context.writeTextBlock("  ::");
         context.writeTextBlock("");
     }
 }
@@ -695,7 +697,9 @@ void KeywordsHelpTopic::writeKeywordListEnd(const HelpWriterContext &context,
     }
     if (!isNullOrEmpty(extraInfo))
     {
-        context.writeTextBlock(extraInfo);
+        std::string fullInfo("  ");
+        fullInfo.append(extraInfo);
+        context.writeTextBlock(fullInfo);
     }
     context.writeTextBlock("");
 }
@@ -714,9 +718,9 @@ void KeywordsHelpTopic::printKeywordList(const HelpWriterContext &context,
         if (method.type == type && bModifiers == bIsModifier)
         {
             const bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
-            const bool bPrintStar
+            const bool bPrintHelpMark
                 = bHasHelp && context.outputFormat() == eHelpOutputFormat_Console;
-            file.writeString(formatString(" %c ", bPrintStar ? '*' : ' '));
+            file.writeString(formatString("   %c ", bPrintHelpMark ? '+' : ' '));
             if (method.help.syntax != NULL)
             {
                 file.writeLine(method.help.syntax);