diff --git a/doc/contributing.rst b/doc/contributing.rst
index b7188a436a..279a4ee941 100644
--- a/doc/contributing.rst
+++ b/doc/contributing.rst
@@ -285,6 +285,15 @@ or
the first is preferred.
+When you specify an *RST role* (highlighting/interpretation marker) do it
+in the postfix form for uniformity, that is after \`text in backticks\`.
+For example an ``:idx:`` role for referencing a topic ("SQLite" in the
+example below) from `Nim Index`_ can be used in doc comment this way:
+
+.. code-block:: nim
+ ## A higher level `SQLite`:idx: database wrapper.
+
+.. _`Nim Index`: https://nim-lang.org/docs/theindex.html
Best practices
==============
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 6845f61b93..d331c2c12d 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -68,7 +68,7 @@
## substitution references, standalone hyperlinks,
## internal links (inline and outline)
## + \`interpreted text\` with roles ``:literal:``, ``:strong:``,
-## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:supscript:``
+## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:``
## (see `RST roles list`_ for description).
## + inline internal targets
##
@@ -1018,6 +1018,16 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) =
for i in countup(0, sep - incr): a.add(n.sons[i])
for i in countup(sep + 1, n.len - 2): b.add(n.sons[i])
+proc whichRole(sym: string): RstNodeKind =
+ case sym
+ of "idx": result = rnIdx
+ of "literal": result = rnInlineLiteral
+ of "strong": result = rnStrongEmphasis
+ of "emphasis": result = rnEmphasis
+ of "sub", "subscript": result = rnSub
+ of "sup", "superscript": result = rnSup
+ else: result = rnGeneralRole
+
proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
var newKind = n.kind
var newSons = n.sons
@@ -1042,22 +1052,8 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
result = newRstNode(newKind, newSons)
elif match(p, p.idx, ":w:"):
# a role:
- if nextTok(p).symbol == "idx":
- newKind = rnIdx
- elif nextTok(p).symbol == "literal":
- newKind = rnInlineLiteral
- elif nextTok(p).symbol == "strong":
- newKind = rnStrongEmphasis
- elif nextTok(p).symbol == "emphasis":
- newKind = rnEmphasis
- elif nextTok(p).symbol == "sub" or
- nextTok(p).symbol == "subscript":
- newKind = rnSub
- elif nextTok(p).symbol == "sup" or
- nextTok(p).symbol == "supscript":
- newKind = rnSup
- else:
- newKind = rnGeneralRole
+ newKind = whichRole(nextTok(p).symbol)
+ if newKind == rnGeneralRole:
let newN = newRstNode(rnInner, n.sons)
newSons = @[newN, newLeaf(nextTok(p).symbol)]
inc p.idx, 3
@@ -1318,6 +1314,12 @@ proc parseInline(p: var RstParser, father: PRstNode) =
var n = newRstNode(rnInlineLiteral)
parseUntil(p, n, "``", false)
father.add(n)
+ elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`":
+ let k = whichRole(nextTok(p).symbol)
+ let n = newRstNode(k)
+ inc p.idx, 3
+ parseUntil(p, n, "`", false) # bug #17260
+ father.add(n)
elif isInlineMarkupStart(p, "`"):
var n = newRstNode(rnInterpretedText)
parseUntil(p, n, "`", false) # bug #17260
@@ -1677,7 +1679,7 @@ proc whichSection(p: RstParser): RstNodeKind =
elif predNL(p) and
currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite:
result = rnBulletList
- elif match(p, p.idx, ":w:") and predNL(p):
+ elif match(p, p.idx, ":w:E") and predNL(p):
# (currentTok(p).symbol == ":")
result = rnFieldList
elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or
diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim
index 0f3890faa7..da01c30d21 100644
--- a/tests/stdlib/trstgen.nim
+++ b/tests/stdlib/trstgen.nim
@@ -1326,6 +1326,47 @@ Test1
output)
check("""
-d | option | """ in
output)
+
+ test "Roles: subscript prefix/postfix":
+ let expected = "See some text."
+ check "See :subscript:`some text`.".toHtml == expected
+ check "See `some text`:subscript:.".toHtml == expected
+
+ test "Roles: correct parsing from beginning of line":
+ let expected = "3He is an isotope of helium."
+ check """:superscript:`3`\ He is an isotope of helium.""".toHtml == expected
+ check """:sup:`3`\ He is an isotope of helium.""".toHtml == expected
+ check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected
+ check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected
+
+ test "(not) Roles: check escaping 1":
+ let expected = """See :subscript:""" &
+ """some text."""
+ check """See \:subscript:`some text`.""".toHtml == expected
+ check """See :subscript\:`some text`.""".toHtml == expected
+
+ test "(not) Roles: check escaping 2":
+ check("""See :subscript:\`some text\`.""".toHtml ==
+ "See :subscript:`some text`.")
+
+ test "Field list":
+ check(":field: text".toHtml ==
+ """""" &
+ """""" &
+ """| field: | """ &
+ """ text |
""" & "\n
")
+
+ test "Field list: body after newline":
+ let output = dedent """
+ :field:
+ text1""".toHtml
+ check "field:" in output
+ check "| text1 | " in output
+
+ test "Field list (incorrect)":
+ check ":field:text".toHtml == ":field:text"
+
suite "RST/Code highlight":
test "Basic Python code highlight":
let pythonCode = """