RST: implement footnotes and citations (#16960)

* RST: implement footnotes and citations
* manual fixup of nimdoc.out.css
* remove unused code
* shorter printing code
* Update lib/packages/docutils/rst.nim

Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
This commit is contained in:
Andrey Makarov 2021-02-15 16:12:40 +03:00 • committed by GitHub
commit 35bd39a9d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 656 additions and 36 deletions

View file

@ -8,6 +8,9 @@ import ../../lib/packages/docutils/rstgen
import ../../lib/packages/docutils/rst
import unittest, strutils, strtabs
proc toHtml(input: string): string =
rstToHtml(input, {roSupportMarkdown}, defaultConfig())
suite "YAML syntax highlighting":
test "Basics":
let input = """.. code-block:: yaml
@ -572,6 +575,203 @@ Test1
doAssert count(output1, "<ul ") == 1
doAssert count(output1, "</ul>") == 1
test "Nim RST footnotes and citations":
# check that auto-label footnote enumerated properly after a manual one
let input1 = dedent """
.. [1] Body1.
.. [#note] Body2
Ref. [#note]_
"""
let output1 = input1.toHtml
doAssert output1.count(">[1]</a>") == 1
doAssert output1.count(">[2]</a>") == 2
doAssert "href=\"#footnote-note\"" in output1
doAssert ">[-1]" notin output1
doAssert "Body1." in output1
doAssert "Body2" in output1
# check that there are NO footnotes/citations, only comments:
let input2 = dedent """
.. [1 #] Body1.
.. [# note] Body2.
.. [wrong citation] That gives you a comment.
.. [not&allowed] That gives you a comment.
Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_.
"""
let output2 = input2.toHtml
doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&amp;allowed]_. "
# check that auto-symbol footnotes work:
let input3 = dedent """
Ref. [*]_ and [*]_ and [*]_.
.. [*] Body1
.. [*] Body2.
.. [*] Body3.
.. [*] Body4
And [*]_.
"""
let output3 = input3.toHtml
# both references and footnotes. Footnotes have link to themselves.
doAssert output3.count("href=\"#footnotesym-1\">[*]</a>") == 2
doAssert output3.count("href=\"#footnotesym-2\">[**]</a>") == 2
doAssert output3.count("href=\"#footnotesym-3\">[***]</a>") == 2
doAssert output3.count("href=\"#footnotesym-4\">[^]</a>") == 2
# footnote group
doAssert output3.count("<hr class=\"footnote\">" &
"<div class=\"footnote-group\">") == 1
# footnotes
doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
"<a href=\"#footnotesym-1\">[*]</a></strong></sup></div>") == 1
doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
"<a href=\"#footnotesym-2\">[**]</a></strong></sup></div>") == 1
doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
"<a href=\"#footnotesym-3\">[***]</a></strong></sup></div>") == 1
doAssert output3.count("<div class=\"footnote-label\"><sup><strong>" &
"<a href=\"#footnotesym-4\">[^]</a></strong></sup></div>") == 1
for i in 1 .. 4: doAssert ("Body" & $i) in output3
# check manual, auto-number and auto-label footnote enumeration
let input4 = dedent """
.. [3] Manual1.
.. [#] Auto-number1.
.. [#mylabel] Auto-label1.
.. [#note] Auto-label2.
.. [#] Auto-number2.
Ref. [#note]_ and [#]_ and [#]_.
"""
let output4 = input4.toHtml
doAssert ">[-1]" notin output1
let order = @[
"footnote-3", "[3]", "Manual1.",
"footnoteauto-1", "[1]", "Auto-number1",
"footnote-mylabel", "[2]", "Auto-label1",
"footnote-note", "[4]", "Auto-label2",
"footnoteauto-2", "[5]", "Auto-number2",
]
for i in 0 .. order.len-2:
let pos1 = output4.find(order[i])
let pos2 = output4.find(order[i+1])
doAssert pos1 >= 0
doAssert pos2 >= 0
doAssert pos1 < pos2
# forgot [#]_
let input5 = dedent """
.. [3] Manual1.
.. [#] Auto-number1.
.. [#note] Auto-label2.
Ref. [#note]_
"""
# TODO: find out hot to configure proper exception instead of defect
expect(AssertionDefect):
let output5 = input5.toHtml
# extra [*]_
let input6 = dedent """
Ref. [*]_
.. [*] Auto-Symbol.
Ref. [*]_
"""
expect(AssertionDefect):
let output6 = input6.toHtml
let input7 = dedent """
.. [Some:CITATION-2020] Citation.
Ref. [some:citation-2020]_.
"""
let output7 = input7.toHtml
doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2
doAssert output7.count("[Some:CITATION-2020]") == 1
doAssert output7.count("[some:citation-2020]") == 1
doAssert output3.count("<hr class=\"footnote\">" &
"<div class=\"footnote-group\">") == 1
let input8 = dedent """
.. [Some] Citation.
Ref. [som]_.
"""
expect(AssertionDefect):
let output8 = input8.toHtml
# check that footnote group does not break parsing of other directives:
let input9 = dedent """
.. [Some] Citation.
.. _`internal anchor`:
.. [Another] Citation.
.. just comment.
.. [Third] Citation.
Paragraph1.
Paragraph2 ref `internal anchor`_.
"""
let output9 = input9.toHtml
#doAssert "id=\"internal-anchor\"" in output9
#doAssert "internal anchor" notin output9
doAssert output9.count("<hr class=\"footnote\">" &
"<div class=\"footnote-group\">") == 1
doAssert output9.count("<div class=\"footnote-label\">") == 3
doAssert "just comment" notin output9
# check that nested citations/footnotes work
let input10 = dedent """
Paragraph1 [#]_.
.. [First] Citation.
.. [#] Footnote.
.. [Third] Citation.
"""
let output10 = input10.toHtml
doAssert output10.count("<hr class=\"footnote\">" &
"<div class=\"footnote-group\">") == 3
doAssert output10.count("<div class=\"footnote-label\">") == 3
doAssert "<a href=\"#citation-first\">[First]</a>" in output10
doAssert "<a href=\"#footnoteauto-1\">[1]</a>" in output10
doAssert "<a href=\"#citation-third\">[Third]</a>" in output10
let input11 = ".. [note]\n" # should not crash
let output11 = input11.toHtml
doAssert "<a href=\"#citation-note\">[note]</a>" in output11
# check that references to auto-numbered footnotes work
let input12 = dedent """
Ref. [#]_ and [#]_ STOP.
.. [#] Body1.
.. [#] Body3
.. [2] Body2.
"""
let output12 = input12.toHtml
let orderAuto = @[
"#footnoteauto-1", "[1]",
"#footnoteauto-2", "[3]",
"STOP.",
"Body1.", "Body3", "Body2."
]
for i in 0 .. orderAuto.len-2:
let pos1 = output12.find(orderAuto[i])
let pos2 = output12.find(orderAuto[i+1])
doAssert pos1 >= 0
doAssert pos2 >= 0
doAssert pos1 < pos2
test "Nim (RST extension) code-block":
# check that presence of fields doesn't consume the following text as
# its code (which is a literal block)
@ -760,6 +960,13 @@ Test1
-----------
Ref. target300_ and target301_.
.. _target103:
.. [cit2020] note.
Ref. target103_.
"""
let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig())
# "target101" should be erased and changed to "section-xyz":
@ -772,6 +979,7 @@ Test1
# links should preserve their original names but point to section labels:
doAssert "href=\"#section-xyz\">target300" in output2
doAssert "href=\"#subsectiona\">target301" in output2
doAssert "href=\"#citation-cit2020\">target103" in output2
let output2l = rstToLatex(input2, {})
doAssert "\\label{section-xyz}\\hypertarget{section-xyz}{}" in output2l