Change naming convention for C++ interfaces
[alexxy/gromacs.git] / src / gromacs / selection / selhelp.cpp
index 4a52815f378e9bc15b1bf8cc47196309d4dc19ff..0a29722728ae9fce6cf1803696842c0e80750bbd 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2009,2010,2011,2012,2013,2014, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013,2014,2015, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
@@ -43,6 +43,7 @@
 
 #include "selhelp.h"
 
+#include <set>
 #include <string>
 #include <utility>
 #include <vector>
@@ -52,8 +53,9 @@
 #include "gromacs/onlinehelp/helptopic.h"
 #include "gromacs/onlinehelp/helpwritercontext.h"
 #include "gromacs/utility/exceptions.h"
-#include "gromacs/utility/file.h"
+#include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
 
 #include "selmethod.h"
 #include "symrec.h"
@@ -74,7 +76,12 @@ const char        CommonHelpText::title[] =
 const char *const CommonHelpText::text[] = {
     "Selections are used to select atoms/molecules/residues for analysis.",
     "In contrast to traditional index files, selections can be dynamic, i.e.,",
-    "select different atoms for different trajectory frames.[PAR]",
+    "select different atoms for different trajectory frames. The GROMACS",
+    "manual contains a short introductory section to selections in the",
+    "Analysis chapter, including suggestions on how to get familiar with",
+    "selections if you are new to the concept. The subtopics listed below",
+    "provide more details on the technical and syntactic aspects of",
+    "selections.[PAR]",
 
     "Each analysis tool requires a different number of selections and the",
     "selections are interpreted differently. The general idea is still the",
@@ -82,16 +89,7 @@ const char *const CommonHelpText::text[] = {
     "can be an atom position or center-of-mass or center-of-geometry of",
     "a set of atoms. The tool then uses these positions for its analysis to",
     "allow very flexible processing. Some analysis tools may have limitations",
-    "on the types of selections allowed.[PAR]",
-
-    "To get started with selections, run, e.g., [TT][PROGRAM] select[tt]",
-    "without specifying selections on the command-line and use the interactive",
-    "prompt to try out different selections.",
-    "This tool provides output options that allow one to see what is actually",
-    "selected by the given selections, and the interactive prompt reports",
-    "syntax errors immediately, allowing one to try again.",
-    "The subtopics listed below give more details on different aspects of",
-    "selections.",
+    "on the types of selections allowed."
 };
 
 struct ArithmeticHelpText
@@ -129,7 +127,7 @@ const char *const CmdLineHelpText::text[] = {
     "if they are complex or for scripting.[PAR]",
 
     "Each tool has different command-line arguments for specifying selections",
-    "(listed by [TT][PROGRAM] help <tool>[tt]).",
+    "(see the help for the individual tools).",
     "You can either pass a single string containing all selections (separated",
     "by semicolons), or multiple strings, each containing one selection.",
     "Note that you need to quote the selections to protect them from the",
@@ -154,16 +152,17 @@ const char *const CmdLineHelpText::text[] = {
     "To use groups from a traditional index file, use argument [TT]-n[tt]",
     "to provide a file. See the \"syntax\" subtopic for how to use them.",
     "If this option is not provided, default groups are generated.",
-    "The default groups are generated by reading selections from a file",
-    "[TT]defselection.dat[tt]. If such a file is found in the current",
-    "directory, it is used instead of the one provided by default.[PAR]",
-
+    "The default groups are generated with the same logic as for",
+    "non-selection tools.",
+    "",
     "Depending on the tool, two additional command-line arguments may be",
-    "available to control the behavior:[BR]",
-    "1. [TT]-seltype[tt] can be used to specify the default type of",
-    "positions to calculate for each selection.[BR]",
-    "2. [TT]-selrpos[tt] can be used to specify the default type of",
-    "positions used in selecting atoms by coordinates.[BR]",
+    "available to control the behavior:",
+    "",
+    "* [TT]-seltype[tt] can be used to specify the default type of",
+    "  positions to calculate for each selection.",
+    "* [TT]-selrpos[tt] can be used to specify the default type of",
+    "  positions used in selecting atoms by coordinates.",
+    "",
     "See the \"positions\" subtopic for more information on these options.",
 };
 
@@ -191,11 +190,13 @@ const char *const EvaluationHelpText::text[] = {
     "Another point for optimization is in common subexpressions: they are not",
     "automatically recognized, but can be manually optimized by the use of",
     "variables. This can have a big impact on the performance of complex",
-    "selections, in particular if you define several index groups like this:",
-    "  [TT]rdist = distance from com of resnr 1 to 5;[tt][BR]",
-    "  [TT]resname RES and rdist < 2;[tt][BR]",
-    "  [TT]resname RES and rdist < 4;[tt][BR]",
-    "  [TT]resname RES and rdist < 6;[tt][BR]",
+    "selections, in particular if you define several index groups like this::",
+    "",
+    "  rdist = distance from com of resnr 1 to 5;",
+    "  resname RES and rdist < 2;",
+    "  resname RES and rdist < 4;",
+    "  resname RES and rdist < 6;",
+    "",
     "Without the variable assignment, the distances would be evaluated three",
     "times, although they are exactly the same within each selection.",
     "Anything assigned into a variable becomes a common subexpression that",
@@ -217,37 +218,90 @@ const char        ExamplesHelpText::name[]  = "examples";
 const char        ExamplesHelpText::title[] =
     "Selection examples";
 const char *const ExamplesHelpText::text[] = {
-    // TODO: Once there are more tools available, use examples that invoke
-    // tools and explain what the selections do in those tools.
-    "Below, examples of increasingly complex selections are given.[PAR]",
-
-    "Selection of all water oxygens:[BR]",
-    "  resname SOL and name OW",
-    "[PAR]",
-
-    "Centers of mass of residues 1 to 5 and 10:[BR]",
-    "  res_com of resnr 1 to 5 10",
-    "[PAR]",
-
-    "All atoms farther than 1 nm of a fixed position:[BR]",
-    "  not within 1 of [1.2, 3.1, 2.4]",
-    "[PAR]",
-
-    "All atoms of a residue LIG within 0.5 nm of a protein (with a custom name):[BR]",
-    "  \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
-    "[PAR]",
-
-    "All protein residues that have at least one atom within 0.5 nm of a residue LIG:[BR]",
-    "  group \"Protein\" and same residue as within 0.5 of resname LIG",
-    "[PAR]",
-
-    "All RES residues whose COM is between 2 and 4 nm from the COM of all of them:[BR]",
-    "  rdist = res_com distance from com of resname RES[BR]",
-    "  resname RES and rdist >= 2 and rdist <= 4",
-    "[PAR]",
-
-    "Selection like C1 C2 C2 C3 C3 C4 ... C8 C9 (e.g., for g_bond):[BR]",
-    "  name \"C[1-8]\" merge name \"C[2-9]\"",
+    "Below, examples of different types of selections are given.",
+    "",
+    "* Selection of all water oxygens::",
+    "",
+    "    resname SOL and name OW",
+    "",
+    "* Centers of mass of residues 1 to 5 and 10::",
+    "",
+    "    res_com of resnr 1 to 5 10",
+    "",
+    "* All atoms farther than 1 nm of a fixed position::",
+    "",
+    "    not within 1 of [1.2, 3.1, 2.4]",
+    "",
+    "* All atoms of a residue LIG within 0.5 nm of a protein (with a custom name)::",
+    "",
+    "    \"Close to protein\" resname LIG and within 0.5 of group \"Protein\"",
+    "",
+    "* All protein residues that have at least one atom within 0.5 nm of a residue LIG::",
+    "",
+    "    group \"Protein\" and same residue as within 0.5 of resname LIG",
+    "",
+    "* All RES residues whose COM is between 2 and 4 nm from the COM of all of them::",
+    "",
+    "    rdist = res_com distance from com of resname RES",
+    "    resname RES and rdist >= 2 and rdist <= 4",
+    "",
+    // TODO: Make it possible to use links below.
+    "* Selection like with duplicate atoms like C1 C2 C2 C3 C3 C4 ... C8 C9::",
+    "",
+    "    name \"C[1-8]\" merge name \"C[2-9]\"",
+    "",
+    "  This can be used with [TT]gmx distance[tt] to compute C1-C2, C2-C3 etc.",
+    "  distances.",
+    "",
+    "* Selection with atoms in order C2 C1::",
+    "",
+    "    name C1 C2 permute 2 1",
+    "",
+    "  This can be used with [TT]gmx gangle[tt] to get C2->C1 vectors instead of",
+    "  C1->C2.",
+    "",
+    "* Selection with COMs of two index groups::",
+    "",
+    "    com of group 1 plus com of group 2",
+    "",
+    "  This can be used with [TT]gmx distance[tt] to compute the distance between",
+    "  these two COMs.",
+    "",
+    "* Fixed vector along x (can be used as a reference with [TT]gmx gangle[tt])::",
+    "",
+    "    [0, 0, 0] plus [1, 0, 0]",
+    "",
+    "* The following examples explain the difference between the various",
+    "  position types.  This selection selects a position for each residue",
+    "  where any of the three atoms C[123] has [TT]x < 2[tt].  The positions",
+    "  are computed as the COM of all three atoms.",
+    "  This is the default behavior if you just write [TT]res_com of[tt]. ::",
+    "",
+    "    part_res_com of name C1 C2 C3 and x < 2",
+    "",
+    "  This selection does the same, but the positions are computed as COM",
+    "  positions of whole residues::",
+    "",
+    "    whole_res_com of name C1 C2 C3 and x < 2",
+    "",
+    "  Finally, this selection selects the same residues, but the positions",
+    "  are computed as COM of exactly those atoms atoms that match the",
+    "  [TT]x < 2[tt] criterion::",
+    "",
+    "    dyn_res_com of name C1 C2 C3 and x < 2",
+    "",
+    "* Without the [TT]of[tt] keyword, the default behavior is different from",
+    "  above, but otherwise the rules are the same::",
+    "",
+    "    name C1 C2 C3 and res_com x < 2",
+    "",
+    "  works as if [TT]whole_res_com[tt] was specified, and selects the three",
+    "  atoms from residues whose COM satisfiex [TT]x < 2[tt].",
+    "  Using ::",
+    "",
+    "    name C1 C2 C3 and part_res_com x < 2",
+    "",
+    "  instead selects residues based on the COM computed from the C[123] atoms.",
 };
 
 struct KeywordsHelpText
@@ -262,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.",
 };
 
@@ -277,26 +331,51 @@ const char        LimitationsHelpText::name[]  = "limitations";
 const char        LimitationsHelpText::title[] =
     "Selection limitations";
 const char *const LimitationsHelpText::text[] = {
-    "Some analysis programs may require a special structure for the input",
-    "selections (e.g., [TT]gmx angle[tt] requires the index group to be made",
-    "of groups of three or four atoms).",
-    "For such programs, it is up to the user to provide a proper selection",
-    "expression that always returns such positions.",
-    "[PAR]",
-
-    "Due to technical reasons, having a negative value as the first value in",
-    "expressions like[BR]",
-    "[TT]charge -1 to -0.7[tt][BR]",
-    "result in a syntax error. A workaround is to write[BR]",
-    "[TT]charge {-1 to -0.7}[tt][BR]",
-    "instead.[PAR]",
-
-    "When [TT]name[tt] selection keyword is used together with PDB input",
-    "files, the behavior may be unintuitive. When Gromacs reads in a PDB",
-    "file, 4 character atom names that start with a digit are transformed",
-    "such that, e.g., 1HG2 becomes HG21, and the latter is what is matched",
-    "by the [TT]name[tt] keyword. Use [TT]pdbname[tt] to match the atom name",
-    "as it appears in the input PDB file.",
+    "* Some analysis programs may require a special structure for the input",
+    "  selections (e.g., some options of [TT]gmx gangle[tt] require the index",
+    "  group to be made of groups of three or four atoms).",
+    "  For such programs, it is up to the user to provide a proper selection",
+    "  expression that always returns such positions.",
+    "",
+    "* All selection keywords select atoms in increasing order, i.e., you can",
+    "  consider them as set operations that in the end return the atoms in",
+    "  sorted numerical order.  For example, the following selections select",
+    "  the same atoms in the same order::",
+    "",
+    "    resname RA RB RC",
+    "    resname RB RC RA",
+    "",
+    "  ::",
+    "",
+    "    atomnr 10 11 12 13",
+    "    atomnr 12 13 10 11",
+    "    atomnr 10 to 13",
+    "    atomnr 13 to 10",
+    "",
+    "  If you need atoms/positions in a different order, you can:",
+    "",
+    "  * use external index groups (for some static selections),",
+    "  * use the [TT]permute[tt] keyword to change the final order, or",
+    "  * use the [TT]merge[tt] or [TT]plus[tt] keywords to compose the",
+    "    final selection from multiple distinct selections.",
+    "",
+    "* Due to technical reasons, having a negative value as the first value in",
+    "  expressions like ::",
+    "",
+    "    charge -1 to -0.7",
+    "",
+    "  result in a syntax error. A workaround is to write ::",
+    "",
+    "    charge {-1 to -0.7}",
+    "",
+    "  instead.",
+    "",
+    "* When [TT]name[tt] selection keyword is used together with PDB input",
+    "  files, the behavior may be unintuitive. When GROMACS reads in a PDB",
+    "  file, 4 character atom names that start with a digit are transformed",
+    "  such that, e.g., 1HG2 becomes HG21, and the latter is what is matched",
+    "  by the [TT]name[tt] keyword. Use [TT]pdbname[tt] to match the atom name",
+    "  as it appears in the input PDB file.",
 };
 
 struct PositionsHelpText
@@ -310,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.",
@@ -377,28 +456,35 @@ const char *const SyntaxHelpText::text[] = {
     "not if you provide the selections, e.g., from a pipe.[PAR]",
 
     "It is possible to use variables to store selection expressions.",
-    "A variable is defined with the following syntax:[BR]",
-    "[TT]VARNAME = EXPR ;[tt][BR]",
+    "A variable is defined with the following syntax::",
+    "",
+    "  VARNAME = EXPR ;",
+    "",
     "where [TT]EXPR[tt] is any valid selection expression.",
     "After this, [TT]VARNAME[tt] can be used anywhere where [TT]EXPR[tt]",
     "would be valid.[PAR]",
 
     "Selections are composed of three main types of expressions, those that",
-    "define atoms ([TT]ATOM_EXPR[tt]s), those that define positions",
-    "([TT]POS_EXPR[tt]s), and those that evaluate to numeric values",
-    "([TT]NUM_EXPR[tt]s). Each selection should be a [TT]POS_EXPR[tt]",
+    "define atoms ([TT]ATOM_EXPR[tt]), those that define positions",
+    "([TT]POS_EXPR[tt]), and those that evaluate to numeric values",
+    "([TT]NUM_EXPR[tt]). Each selection should be a [TT]POS_EXPR[tt]",
     "or a [TT]ATOM_EXPR[tt] (the latter is automatically converted to",
-    "positions). The basic rules are as follows:[BR]",
-    "1. An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
-    "[TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
-    "is true.[BR]",
-    "2. Atom expressions can be combined with boolean operations such as",
-    "[TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
-    "[TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
-    "evaluation order.[BR]",
-    "3. [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
-    "expressions in various ways, see the \"positions\" subtopic for more",
-    "details.[PAR]",
+    "positions). The basic rules are as follows:",
+    "",
+    "* An expression like [TT]NUM_EXPR1 < NUM_EXPR2[tt] evaluates to an",
+    "  [TT]ATOM_EXPR[tt] that selects all the atoms for which the comparison",
+    "  is true.",
+    "* Atom expressions can be combined with boolean operations such as",
+    "  [TT]not ATOM_EXPR[tt], [TT]ATOM_EXPR and ATOM_EXPR[tt], or",
+    "  [TT]ATOM_EXPR or ATOM_EXPR[tt]. Parentheses can be used to alter the",
+    "  evaluation order.",
+    "* [TT]ATOM_EXPR[tt] expressions can be converted into [TT]POS_EXPR[tt]",
+    "  expressions in various ways, see the \"positions\" subtopic for more",
+    "  details.",
+    "* [TT]POS_EXPR[tt] can be converted into [TT]NUM_EXPR[tt] using syntax",
+    "  like \"[TT]x of POS_EXPR[tt]\". Currently, this is only supported for single",
+    "  positions like in expression \"[TT]x of cog of ATOM_EXPR[tt]\".",
+    "",
 
     "Some keywords select atoms based on string values such as the atom name.",
     "For these keywords, it is possible to use wildcards ([TT]name \"C*\"[tt])",
@@ -456,13 +542,14 @@ class KeywordDetailsHelpTopic : public AbstractSimpleHelpTopic
         }
         virtual const char *title() const
         {
-            return NULL;
+            return method_.help.helpTitle;
         }
 
     protected:
         virtual std::string helpText() const
         {
-            return concatenateStrings(method_.help.help, method_.help.nlhelp);
+            return joinStrings(method_.help.help,
+                               method_.help.help + method_.help.nlhelp, "\n");
         }
 
     private:
@@ -498,6 +585,17 @@ class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
                                       const gmx_ana_selmethod_t *> >
             MethodList;
 
+        /*! \brief
+         * Prints markup for starting a list of keywords.
+         */
+        void writeKeywordListStart(const HelpWriterContext &context,
+                                   const char              *heading) const;
+        /*! \brief
+         * Prints markup for ending a list of keywords.
+         */
+        void writeKeywordListEnd(const HelpWriterContext &context,
+                                 const char              *extraInfo) const;
+
         /*! \brief
          * Prints a brief list of keywords (selection methods) available.
          *
@@ -509,6 +607,11 @@ class KeywordsHelpTopic : public CompositeHelpTopic<KeywordsHelpText>
         void printKeywordList(const HelpWriterContext &context,
                               e_selvalue_t type, bool bModifiers) const;
 
+        /*! \brief
+         * Prints the detailed help for keywords for rst export.
+         */
+        void writeKeywordSubTopics(const HelpWriterContext &context) const;
+
         MethodList              methods_;
 };
 
@@ -539,66 +642,85 @@ KeywordsHelpTopic::KeywordsHelpTopic()
 
 void KeywordsHelpTopic::writeHelp(const HelpWriterContext &context) const
 {
-    if (context.outputFormat() != eHelpOutputFormat_Console)
-    {
-        GMX_THROW(NotImplementedError(
-                          "Selection help is not implemented for this output format"));
-    }
-    // TODO: The markup here is not really appropriate, and printKeywordList()
-    // still prints raw text, but these are waiting for discussion of the
-    // markup format in #969.
-    writeBasicHelpTopic(context, *this, helpText());
-    context.writeTextBlock("[BR]");
+    context.writeTextBlock(helpText());
+    context.writeTextBlock("");
 
     // Print the list of keywords
-    context.writeTextBlock(
-            "Keywords that select atoms by an integer property:[BR]"
-            "(use in expressions or like \"atomnr 1 to 5 7 9\")[BR]");
+    writeKeywordListStart(context, "Keywords that select atoms by an integer property:");
     printKeywordList(context, INT_VALUE, false);
-    context.writeTextBlock("[BR]");
+    writeKeywordListEnd(context, "(use in expressions or like \"atomnr 1 to 5 7 9\")");
 
-    context.writeTextBlock(
-            "Keywords that select atoms by a numeric property:[BR]"
-            "(use in expressions or like \"occupancy 0.5 to 1\")[BR]");
+    writeKeywordListStart(context, "Keywords that select atoms by a numeric property:");
     printKeywordList(context, REAL_VALUE, false);
-    context.writeTextBlock("[BR]");
+    writeKeywordListEnd(context, "(use in expressions or like \"occupancy 0.5 to 1\")");
 
-    context.writeTextBlock(
-            "Keywords that select atoms by a string property:[BR]"
-            "(use like \"name PATTERN [PATTERN] ...\")[BR]");
+    writeKeywordListStart(context, "Keywords that select atoms by a string property:");
     printKeywordList(context, STR_VALUE, false);
-    context.writeTextBlock("[BR]");
+    writeKeywordListEnd(context, "(use like \"name PATTERN [PATTERN] ...\")");
 
-    context.writeTextBlock(
-            "Additional keywords that directly select atoms:[BR]");
+    writeKeywordListStart(context, "Additional keywords that directly select atoms:");
     printKeywordList(context, GROUP_VALUE, false);
-    context.writeTextBlock("[BR]");
+    writeKeywordListEnd(context, NULL);
 
-    context.writeTextBlock(
-            "Keywords that directly evaluate to positions:[BR]"
-            "(see also \"positions\" subtopic)[BR]");
+    writeKeywordListStart(context, "Keywords that directly evaluate to positions:");
     printKeywordList(context, POS_VALUE, false);
-    context.writeTextBlock("[BR]");
+    writeKeywordListEnd(context, "(see also \"positions\" subtopic)");
 
-    context.writeTextBlock("Additional keywords:[BR]");
+    writeKeywordListStart(context, "Additional keywords:");
     printKeywordList(context, POS_VALUE, true);
     printKeywordList(context, NO_VALUE, true);
+    writeKeywordListEnd(context, NULL);
+
+    writeKeywordSubTopics(context);
+}
+
+void KeywordsHelpTopic::writeKeywordListStart(const HelpWriterContext &context,
+                                              const char              *heading) const
+{
+    std::string fullHeading("* ");
+    fullHeading.append(heading);
+    context.writeTextBlock(fullHeading);
+    if (context.outputFormat() == eHelpOutputFormat_Rst)
+    {
+        context.writeTextBlock("");
+        context.writeTextBlock("  ::");
+        context.writeTextBlock("");
+    }
+}
+
+void KeywordsHelpTopic::writeKeywordListEnd(const HelpWriterContext &context,
+                                            const char              *extraInfo) const
+{
+    if (context.outputFormat() == eHelpOutputFormat_Rst)
+    {
+        context.writeTextBlock("");
+    }
+    if (!isNullOrEmpty(extraInfo))
+    {
+        std::string fullInfo("  ");
+        fullInfo.append(extraInfo);
+        context.writeTextBlock(fullInfo);
+    }
+    context.writeTextBlock("");
 }
 
 void KeywordsHelpTopic::printKeywordList(const HelpWriterContext &context,
                                          e_selvalue_t             type,
                                          bool                     bModifiers) const
 {
-    File &file = context.outputFile();
+    TextWriter                &file = context.outputFile();
     MethodList::const_iterator iter;
     for (iter = methods_.begin(); iter != methods_.end(); ++iter)
     {
         const gmx_ana_selmethod_t &method = *iter->second;
-        bool bIsModifier                  = (method.flags & SMETH_MODIFIER) != 0;
+        const bool                 bIsModifier
+            = (method.flags & SMETH_MODIFIER) != 0;
         if (method.type == type && bModifiers == bIsModifier)
         {
-            bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
-            file.writeString(formatString(" %c ", bHasHelp ? '*' : ' '));
+            const bool bHasHelp = (method.help.nlhelp > 0 && method.help.help != NULL);
+            const bool bPrintHelpMark
+                = bHasHelp && context.outputFormat() == eHelpOutputFormat_Console;
+            file.writeString(formatString("   %c ", bPrintHelpMark ? '+' : ' '));
             if (method.help.syntax != NULL)
             {
                 file.writeLine(method.help.syntax);
@@ -616,20 +738,65 @@ void KeywordsHelpTopic::printKeywordList(const HelpWriterContext &context,
     }
 }
 
+void KeywordsHelpTopic::writeKeywordSubTopics(const HelpWriterContext &context) const
+{
+    if (context.outputFormat() != eHelpOutputFormat_Rst)
+    {
+        return;
+    }
+    std::set<std::string>      usedSymbols;
+    MethodList::const_iterator iter;
+    for (iter = methods_.begin(); iter != methods_.end(); ++iter)
+    {
+        const gmx_ana_selmethod_t &method = *iter->second;
+        const bool                 bHasHelp
+            = (method.help.nlhelp > 0 && method.help.help != NULL);
+        if (!bHasHelp || usedSymbols.count(iter->first) > 0)
+        {
+            continue;
+        }
+
+        std::string                title;
+        if (method.help.helpTitle != NULL)
+        {
+            title = method.help.helpTitle;
+            title.append(" - ");
+        }
+        title.append(iter->first);
+        MethodList::const_iterator mergeIter = iter;
+        for (++mergeIter; mergeIter != methods_.end(); ++mergeIter)
+        {
+            if (mergeIter->second->help.help == method.help.help)
+            {
+                title.append(", ");
+                title.append(mergeIter->first);
+                usedSymbols.insert(mergeIter->first);
+            }
+        }
+
+        const IHelpTopic         *subTopic = findSubTopic(iter->first.c_str());
+        GMX_RELEASE_ASSERT(subTopic != NULL, "Keyword subtopic no longer exists");
+        HelpWriterContext         subContext(context);
+        subContext.enterSubSection(title);
+        subTopic->writeHelp(subContext);
+        context.writeTextBlock("");
+    }
+}
+
 }   // namespace
 
 //! \cond libapi */
 HelpTopicPointer createSelectionHelpTopic()
 {
     CompositeHelpTopicPointer root(new CompositeHelpTopic<CommonHelpText>);
-    root->registerSubTopic<SimpleHelpTopic<ArithmeticHelpText> >();
     root->registerSubTopic<SimpleHelpTopic<CmdLineHelpText> >();
-    root->registerSubTopic<SimpleHelpTopic<EvaluationHelpText> >();
-    root->registerSubTopic<SimpleHelpTopic<ExamplesHelpText> >();
+    root->registerSubTopic<SimpleHelpTopic<SyntaxHelpText> >();
+    root->registerSubTopic<SimpleHelpTopic<PositionsHelpText> >();
+    root->registerSubTopic<SimpleHelpTopic<ArithmeticHelpText> >();
     root->registerSubTopic<KeywordsHelpTopic>();
+    root->registerSubTopic<SimpleHelpTopic<EvaluationHelpText> >();
     root->registerSubTopic<SimpleHelpTopic<LimitationsHelpText> >();
-    root->registerSubTopic<SimpleHelpTopic<PositionsHelpText> >();
-    root->registerSubTopic<SimpleHelpTopic<SyntaxHelpText> >();
+    root->registerSubTopic<SimpleHelpTopic<ExamplesHelpText> >();
     return move(root);
 }
 //! \endcond