discard """
output: '''
[Suite] RST parsing
[Suite] RST indentation
[Suite] Warnings
[Suite] RST include directive
[Suite] RST escaping
[Suite] RST inline markup
'''
"""
# tests for rst module
import ../../lib/packages/docutils/[rstgen, rst, rstast]
import unittest, strutils
import std/private/miscdollars
import os
proc toAst(input: string,
rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile},
error: ref string = nil,
warnings: ref seq[string] = nil): string =
## If `error` is nil then no errors should be generated.
## The same goes for `warnings`.
proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
arg: string) =
let mc = msgkind.whichMsgClass
let a = $msgkind % arg
var message: string
toLocation(message, filename, line, col + ColRstOffset)
message.add " $1: $2" % [$mc, a]
if mc == mcError:
if error == nil:
raise newException(EParseError, "[unexpected error] " & message)
error[] = message
# we check only first error because subsequent ones may be meaningless
raise newException(EParseError, "")
else:
doAssert warnings != nil, "unexpected RST warning '" & message & "'"
warnings[].add message
try:
const filen = "input"
proc myFindFile(filename: string): string =
# we don't find any files in online mode:
result = ""
var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit,
rstOptions, myFindFile, testMsgHandler)
result = treeRepr(rst)
except EParseError as e:
if e.msg != "":
result = e.msg
suite "RST parsing":
test "References are whitespace-neutral and case-insensitive":
# refname is 'lexical-analysis', the same for all the 3 variants:
check(dedent"""
Lexical Analysis
================
Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_.
""".toAst ==
dedent"""
rnInner
rnHeadline level=1
rnLeaf 'Lexical'
rnLeaf ' '
rnLeaf 'Analysis'
rnParagraph
rnLeaf 'Ref'
rnLeaf '.'
rnLeaf ' '
rnInternalRef
rnInner
rnLeaf 'Lexical'
rnLeaf ' '
rnLeaf 'Analysis'
rnLeaf 'lexical-analysis'
rnLeaf ' '
rnLeaf 'or'
rnLeaf ' '
rnInternalRef
rnInner
rnLeaf 'Lexical'
rnLeaf ' '
rnLeaf 'analysis'
rnLeaf 'lexical-analysis'
rnLeaf ' '
rnLeaf 'or'
rnLeaf ' '
rnInternalRef
rnInner
rnLeaf 'lexical'
rnLeaf ' '
rnLeaf 'analysis'
rnLeaf 'lexical-analysis'
rnLeaf '.'
rnLeaf ' '
""")
test "RST quoted literal blocks":
let expected =
dedent"""
rnInner
rnLeaf 'Paragraph'
rnLeaf ':'
rnLiteralBlock
rnLeaf '>x'
"""
check(dedent"""
Paragraph::
>x""".toAst == expected)
check(dedent"""
Paragraph::
>x""".toAst == expected)
test "RST quoted literal blocks, :: at a separate line":
let expected =
dedent"""
rnInner
rnInner
rnLeaf 'Paragraph'
rnLiteralBlock
rnLeaf '>x
>>y'
"""
check(dedent"""
Paragraph
::
>x
>>y""".toAst == expected)
check(dedent"""
Paragraph
::
>x
>>y""".toAst == expected)
test "Markdown quoted blocks":
check(dedent"""
Paragraph.
>x""".toAst ==
dedent"""
rnInner
rnLeaf 'Paragraph'
rnLeaf '.'
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnLeaf 'x'
""")
# bug #17987
check(dedent"""
foo https://github.com/nim-lang/Nim/issues/8258
> bar""".toAst ==
dedent"""
rnInner
rnInner
rnLeaf 'foo'
rnLeaf ' '
rnStandaloneHyperlink
rnLeaf 'https://github.com/nim-lang/Nim/issues/8258'
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnLeaf 'bar'
""")
let expected = dedent"""
rnInner
rnLeaf 'Paragraph'
rnLeaf '.'
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnLeaf 'x1'
rnLeaf ' '
rnLeaf 'x2'
rnMarkdownBlockQuoteItem quotationDepth=2
rnInner
rnLeaf 'y1'
rnLeaf ' '
rnLeaf 'y2'
rnMarkdownBlockQuoteItem quotationDepth=1
rnLeaf 'z'
"""
check(dedent"""
Paragraph.
>x1 x2
>>y1 y2
>z""".toAst == expected)
check(dedent"""
Paragraph.
> x1 x2
>> y1 y2
> z""".toAst == expected)
check(dedent"""
>x
>y
>z""".toAst ==
dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnLeaf 'x'
rnLeaf ' '
rnLeaf 'y'
rnLeaf ' '
rnLeaf 'z'
""")
check(dedent"""
> z
> > >y
""".toAst ==
dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnLeaf 'z'
rnMarkdownBlockQuoteItem quotationDepth=3
rnLeaf 'y'
""")
test "Markdown quoted blocks: lazy":
let expected = dedent"""
rnInner
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=2
rnInner
rnLeaf 'x'
rnLeaf ' '
rnLeaf 'continuation1'
rnLeaf ' '
rnLeaf 'continuation2'
rnParagraph
rnLeaf 'newParagraph'
"""
check(dedent"""
>>x
continuation1
continuation2
newParagraph""".toAst == expected)
check(dedent"""
>> x
continuation1
continuation2
newParagraph""".toAst == expected)
# however mixing more than 1 non-lazy line and lazy one(s) splits quote
# in our parser, which appeared the easiest way to handle such cases:
var warnings = new seq[string]
check(dedent"""
>> x
>> continuation1
continuation2
newParagraph""".toAst(warnings=warnings) ==
dedent"""
rnInner
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=2
rnLeaf 'x'
rnMarkdownBlockQuoteItem quotationDepth=2
rnInner
rnLeaf 'continuation1'
rnLeaf ' '
rnLeaf 'continuation2'
rnParagraph
rnLeaf 'newParagraph'
""")
check(warnings[] == @[
"input(2, 1) Warning: RST style: two or more quoted lines " &
"are followed by unquoted line 3"])
test "Markdown quoted blocks: not lazy":
# here is where we deviate from CommonMark specification: 'bar' below is
# not considered as continuation of 2-level '>> foo' quote.
check(dedent"""
>>> foo
> bar
>> baz
""".toAst() ==
dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=3
rnLeaf 'foo'
rnMarkdownBlockQuoteItem quotationDepth=1
rnLeaf 'bar'
rnMarkdownBlockQuoteItem quotationDepth=2
rnLeaf 'baz'
""")
test "Markdown quoted blocks: inline markup works":
check(dedent"""
> hi **bold** text
""".toAst == dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnLeaf 'hi'
rnLeaf ' '
rnStrongEmphasis
rnLeaf 'bold'
rnLeaf ' '
rnLeaf 'text'
""")
test "Markdown quoted blocks: blank line separator":
let expected = dedent"""
rnInner
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnLeaf 'x'
rnLeaf ' '
rnLeaf 'y'
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnLeaf 'z'
rnLeaf ' '
rnLeaf 't'
"""
check(dedent"""
>x
>y
> z
> t""".toAst == expected)
check(dedent"""
>x
y
> z
t""".toAst == expected)
test "Markdown quoted blocks: nested body blocks/elements work #1":
let expected = dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnBulletList
rnBulletItem
rnInner
rnLeaf 'x'
rnBulletItem
rnInner
rnLeaf 'y'
"""
check(dedent"""
> - x
- y
""".toAst == expected)
# TODO: if bug #17340 point 28 is resolved then this may work:
# check(dedent"""
# > - x
# - y
# """.toAst == expected)
check(dedent"""
> - x
> - y
""".toAst == expected)
check(dedent"""
>
> - x
>
> - y
>
""".toAst == expected)
test "Markdown quoted blocks: nested body blocks/elements work #2":
let expected = dedent"""
rnAdmonition adType=note
[nil]
[nil]
rnDefList
rnDefItem
rnDefName
rnLeaf 'deflist'
rnLeaf ':'
rnDefBody
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=2
rnInner
rnLeaf 'quote'
rnLeaf ' '
rnLeaf 'continuation'
"""
check(dedent"""
.. Note:: deflist:
>> quote
continuation
""".toAst == expected)
check(dedent"""
.. Note::
deflist:
>> quote
continuation
""".toAst == expected)
check(dedent"""
.. Note::
deflist:
>> quote
>> continuation
""".toAst == expected)
# spaces are not significant between `>`:
check(dedent"""
.. Note::
deflist:
> > quote
> > continuation
""".toAst == expected)
test "Markdown quoted blocks: de-indent handled well":
check(dedent"""
>
> - x
> - y
>
> Paragraph.
""".toAst == dedent"""
rnMarkdownBlockQuote
rnMarkdownBlockQuoteItem quotationDepth=1
rnInner
rnBlockQuote
rnBulletList
rnBulletItem
rnInner
rnLeaf 'x'
rnBulletItem
rnInner
rnLeaf 'y'
rnParagraph
rnLeaf 'Paragraph'
rnLeaf '.'
""")
test "option list has priority over definition list":
check(dedent"""
--defusages
file
-o set
""".toAst ==
dedent"""
rnOptionList
rnOptionListItem order=1
rnOptionGroup
rnLeaf '--'
rnLeaf 'defusages'
rnDescription
rnInner
rnLeaf 'file'
rnOptionListItem order=2
rnOptionGroup
rnLeaf '-'
rnLeaf 'o'
rnDescription
rnLeaf 'set'
""")
test "items of 1 option list can be separated by blank lines":
check(dedent"""
-a desc1
-b desc2
""".toAst ==
dedent"""
rnOptionList
rnOptionListItem order=1
rnOptionGroup
rnLeaf '-'
rnLeaf 'a'
rnDescription
rnLeaf 'desc1'
rnOptionListItem order=2
rnOptionGroup
rnLeaf '-'
rnLeaf 'b'
rnDescription
rnLeaf 'desc2'
""")
test "option list has priority over definition list":
check(dedent"""
defName
defBody
-b desc2
""".toAst ==
dedent"""
rnInner
rnDefList
rnDefItem
rnDefName
rnLeaf 'defName'
rnDefBody
rnInner
rnLeaf 'defBody'
rnOptionList
rnOptionListItem order=1
rnOptionGroup
rnLeaf '-'
rnLeaf 'b'
rnDescription
rnLeaf 'desc2'
""")
test "RST comment":
check(dedent"""
.. comment1
comment2
someParagraph""".toAst ==
dedent"""
rnLeaf 'someParagraph'
""")
check(dedent"""
..
comment1
comment2
someParagraph""".toAst ==
dedent"""
rnLeaf 'someParagraph'
""")
test "check that additional line right after .. ends comment":
check(dedent"""
..
notAcomment1
notAcomment2
someParagraph""".toAst ==
dedent"""
rnInner
rnBlockQuote
rnInner
rnLeaf 'notAcomment1'
rnLeaf ' '
rnLeaf 'notAcomment2'
rnParagraph
rnLeaf 'someParagraph'
""")
test "but blank lines after 2nd non-empty line don't end the comment":
check(dedent"""
..
comment1
comment2
someParagraph""".toAst ==
dedent"""
rnLeaf 'someParagraph'
""")
test "using .. as separator b/w directives and block quotes":
check(dedent"""
.. note:: someNote
..
someBlockQuote""".toAst ==
dedent"""
rnInner
rnAdmonition adType=note
[nil]
[nil]
rnLeaf 'someNote'
rnBlockQuote
rnInner
rnLeaf 'someBlockQuote'
""")
test "no redundant blank lines in literal blocks":
check(dedent"""
Check::
code
""".toAst ==
dedent"""
rnInner
rnLeaf 'Check'
rnLeaf ':'
rnLiteralBlock
rnLeaf 'code'
""")
suite "RST indentation":
test "nested bullet lists":
let input = dedent """
* - bullet1
- bullet2
* - bullet3
- bullet4
"""
let output = input.toAst
check(output == dedent"""
rnBulletList
rnBulletItem
rnBulletList
rnBulletItem
rnInner
rnLeaf 'bullet1'
rnBulletItem
rnInner
rnLeaf 'bullet2'
rnBulletItem
rnBulletList
rnBulletItem
rnInner
rnLeaf 'bullet3'
rnBulletItem
rnInner
rnLeaf 'bullet4'
""")
test "nested markup blocks":
let input = dedent"""
#) .. Hint:: .. Error:: none
#) .. Warning:: term0
Definition0
#) some
paragraph1
#) term1
Definition1
term2
Definition2
"""
check(input.toAst == dedent"""
rnEnumList labelFmt=1)
rnEnumItem
rnAdmonition adType=hint
[nil]
[nil]
rnAdmonition adType=error
[nil]
[nil]
rnLeaf 'none'
rnEnumItem
rnAdmonition adType=warning
[nil]
[nil]
rnDefList
rnDefItem
rnDefName
rnLeaf 'term0'
rnDefBody
rnInner
rnLeaf 'Definition0'
rnEnumItem
rnInner
rnLeaf 'some'
rnLeaf ' '
rnLeaf 'paragraph1'
rnEnumItem
rnDefList
rnDefItem
rnDefName
rnLeaf 'term1'
rnDefBody
rnInner
rnLeaf 'Definition1'
rnDefItem
rnDefName
rnLeaf 'term2'
rnDefBody
rnInner
rnLeaf 'Definition2'
""")
test "code-block parsing":
let input1 = dedent"""
.. code-block:: nim
:test: "nim c $1"
template additive(typ: typedesc) =
discard
"""
let input2 = dedent"""
.. code-block:: nim
:test: "nim c $1"
template additive(typ: typedesc) =
discard
"""
let input3 = dedent"""
.. code-block:: nim
:test: "nim c $1"
template additive(typ: typedesc) =
discard
"""
let inputWrong = dedent"""
.. code-block:: nim
:test: "nim c $1"
template additive(typ: typedesc) =
discard
"""
let ast = dedent"""
rnCodeBlock
rnDirArg
rnLeaf 'nim'
rnFieldList
rnField
rnFieldName
rnLeaf 'test'
rnFieldBody
rnInner
rnLeaf '"'
rnLeaf 'nim'
rnLeaf ' '
rnLeaf 'c'
rnLeaf ' '
rnLeaf '$'
rnLeaf '1'
rnLeaf '"'
rnField
rnFieldName
rnLeaf 'default-language'
rnFieldBody
rnLeaf 'Nim'
rnLiteralBlock
rnLeaf 'template additive(typ: typedesc) =
discard'
"""
check input1.toAst == ast
check input2.toAst == ast
check input3.toAst == ast
# "template..." should be parsed as a definition list attached to ":test:":
check inputWrong.toAst != ast
suite "Warnings":
test "warnings for broken footnotes/links/substitutions":
let input = dedent"""
firstParagraph
footnoteRef [som]_
link `a broken Link`_
substitution |undefined subst|
link short.link_
lastParagraph
"""
var warnings = new seq[string]
let output = input.toAst(warnings=warnings)
check(warnings[] == @[
"input(3, 14) Warning: broken link 'citation-som'",
"input(5, 7) Warning: broken link 'a broken Link'",
"input(7, 15) Warning: unknown substitution 'undefined subst'",
"input(9, 6) Warning: broken link 'short.link'"
])
test "With include directive and blank lines at the beginning":
"other.rst".writeFile(dedent"""
firstParagraph
here brokenLink_""")
let input = ".. include:: other.rst"
var warnings = new seq[string]
let output = input.toAst(warnings=warnings)
check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"]
check(output == dedent"""
rnInner
rnParagraph
rnLeaf 'firstParagraph'
rnParagraph
rnLeaf 'here'
rnLeaf ' '
rnRef
rnLeaf 'brokenLink'
""")
removeFile("other.rst")
test "warnings for ambiguous links (references + anchors)":
# Reference like `x`_ generates a link alias x that may clash with others
let input = dedent"""
Manual reference: `foo <#foo,string,string>`_
.. _foo:
Paragraph.
Ref foo_
"""
var warnings = new seq[string]
let output = input.toAst(warnings=warnings)
check(warnings[] == @[
dedent """
input(7, 5) Warning: ambiguous doc link `foo`
clash:
(3, 8): (manual directive anchor)
(1, 45): (implicitly-generated hyperlink alias)"""
])
# reference should be resolved to the manually set anchor:
check(output ==
dedent"""
rnInner
rnParagraph
rnLeaf 'Manual'
rnLeaf ' '
rnLeaf 'reference'
rnLeaf ':'
rnLeaf ' '
rnHyperlink
rnInner
rnLeaf 'foo'
rnInner
rnLeaf '#'
rnLeaf 'foo'
rnLeaf ','
rnLeaf 'string'
rnLeaf ','
rnLeaf 'string'
rnParagraph anchor='foo'
rnLeaf 'Paragraph'
rnLeaf '.'
rnParagraph
rnLeaf 'Ref'
rnLeaf ' '
rnInternalRef
rnInner
rnLeaf 'foo'
rnLeaf 'foo'
rnLeaf ' '
""")
suite "RST include directive":
test "Include whole":
"other.rst".writeFile("**test1**")
let input = ".. include:: other.rst"
doAssert "test1" == rstTohtml(input, {}, defaultConfig())
removeFile("other.rst")
test "Include starting from":
"other.rst".writeFile("""
And this should **NOT** be visible in `docs.html`
OtherStart
*Visible*
""")
let input = """
.. include:: other.rst
:start-after: OtherStart
"""
check "Visible" == rstTohtml(input, {}, defaultConfig())
removeFile("other.rst")
test "Include everything before":
"other.rst".writeFile("""
*Visible*
OtherEnd
And this should **NOT** be visible in `docs.html`
""")
let input = """
.. include:: other.rst
:end-before: OtherEnd
"""
doAssert "Visible" == rstTohtml(input, {}, defaultConfig())
removeFile("other.rst")
test "Include everything between":
"other.rst".writeFile("""
And this should **NOT** be visible in `docs.html`
OtherStart
*Visible*
OtherEnd
And this should **NOT** be visible in `docs.html`
""")
let input = """
.. include:: other.rst
:start-after: OtherStart
:end-before: OtherEnd
"""
check "Visible" == rstTohtml(input, {}, defaultConfig())
removeFile("other.rst")
test "Ignore premature ending string":
"other.rst".writeFile("""
OtherEnd
And this should **NOT** be visible in `docs.html`
OtherStart
*Visible*
OtherEnd
And this should **NOT** be visible in `docs.html`
""")
let input = """
.. include:: other.rst
:start-after: OtherStart
:end-before: OtherEnd
"""
doAssert "Visible" == rstTohtml(input, {}, defaultConfig())
removeFile("other.rst")
suite "RST escaping":
test "backspaces":
check("""\ this""".toAst == dedent"""
rnLeaf 'this'
""")
check("""\\ this""".toAst == dedent"""
rnInner
rnLeaf '\'
rnLeaf ' '
rnLeaf 'this'
""")
check("""\\\ this""".toAst == dedent"""
rnInner
rnLeaf '\'
rnLeaf 'this'
""")
check("""\\\\ this""".toAst == dedent"""
rnInner
rnLeaf '\'
rnLeaf '\'
rnLeaf ' '
rnLeaf 'this'
""")
suite "RST inline markup":
test "* and ** surrounded by spaces are not inline markup":
check("a * b * c ** d ** e".toAst == dedent"""
rnInner
rnLeaf 'a'
rnLeaf ' '
rnLeaf '*'
rnLeaf ' '
rnLeaf 'b'
rnLeaf ' '
rnLeaf '*'
rnLeaf ' '
rnLeaf 'c'
rnLeaf ' '
rnLeaf '**'
rnLeaf ' '
rnLeaf 'd'
rnLeaf ' '
rnLeaf '**'
rnLeaf ' '
rnLeaf 'e'
""")
test "end-string has repeating symbols":
check("*emphasis content****".toAst == dedent"""
rnEmphasis
rnLeaf 'emphasis'
rnLeaf ' '
rnLeaf 'content'
rnLeaf '***'
""")
check("""*emphasis content\****""".toAst == dedent"""
rnEmphasis
rnLeaf 'emphasis'
rnLeaf ' '
rnLeaf 'content'
rnLeaf '*'
rnLeaf '**'
""") # exact configuration of leafs with * is not really essential,
# only total number of * is essential
check("**strong content****".toAst == dedent"""
rnStrongEmphasis
rnLeaf 'strong'
rnLeaf ' '
rnLeaf 'content'
rnLeaf '**'
""")
check("""**strong content*\****""".toAst == dedent"""
rnStrongEmphasis
rnLeaf 'strong'
rnLeaf ' '
rnLeaf 'content'
rnLeaf '*'
rnLeaf '*'
rnLeaf '*'
""")
check("``lit content`````".toAst == dedent"""
rnInlineLiteral
rnLeaf 'lit'
rnLeaf ' '
rnLeaf 'content'
rnLeaf '```'
""")
test "interpreted text parsing: code fragments":
check(dedent"""
.. default-role:: option
`--gc:refc`""".toAst ==
dedent"""
rnInner
rnDefaultRole
rnDirArg
rnLeaf 'option'
[nil]
[nil]
rnParagraph
rnCodeFragment
rnInner
rnLeaf '--'
rnLeaf 'gc'
rnLeaf ':'
rnLeaf 'refc'
rnLeaf 'option'
""")
test """interpreted text can be ended with \` """:
let output = (".. default-role:: literal\n" & """`\``""").toAst
check(output.endsWith """
rnParagraph
rnInlineLiteral
rnLeaf '`'""" & "\n")
let output2 = """`\``""".toAst
check(output2 == dedent"""
rnInlineCode
rnDirArg
rnLeaf 'nim'
[nil]
rnLiteralBlock
rnLeaf '`'
""")
let output3 = """`proc \`+\``""".toAst
check(output3 == dedent"""
rnInlineCode
rnDirArg
rnLeaf 'nim'
[nil]
rnLiteralBlock
rnLeaf 'proc `+`'
""")
check("""`\\`""".toAst ==
dedent"""
rnInlineCode
rnDirArg
rnLeaf 'nim'
[nil]
rnLiteralBlock
rnLeaf '\\'
""")
test "Markdown-style code/backtick":
# no whitespace is required before `
check("`try`...`except`".toAst ==
dedent"""
rnInner
rnInlineCode
rnDirArg
rnLeaf 'nim'
[nil]
rnLiteralBlock
rnLeaf 'try'
rnLeaf '...'
rnInlineCode
rnDirArg
rnLeaf 'nim'
[nil]
rnLiteralBlock
rnLeaf 'except'
""")
test """inline literals can contain \ anywhere""":
check("""``\``""".toAst == dedent"""
rnInlineLiteral
rnLeaf '\'
""")
check("""``\\``""".toAst == dedent"""
rnInlineLiteral
rnLeaf '\'
rnLeaf '\'
""")
check("""``\```""".toAst == dedent"""
rnInlineLiteral
rnLeaf '\'
rnLeaf '`'
""")
check("""``\\```""".toAst == dedent"""
rnInlineLiteral
rnLeaf '\'
rnLeaf '\'
rnLeaf '`'
""")
check("""``\````""".toAst == dedent"""
rnInlineLiteral
rnLeaf '\'
rnLeaf '`'
rnLeaf '`'
""")
test "references with _ at the end":
check(dedent"""
.. _lnk: https
lnk_""".toAst ==
dedent"""
rnHyperlink
rnInner
rnLeaf 'lnk'
rnInner
rnLeaf 'https'
""")
test "not a hyper link":
check(dedent"""
.. _lnk: https
lnk___""".toAst ==
dedent"""
rnInner
rnLeaf 'lnk'
rnLeaf '___'
""")
test "no punctuation in the end of a standalone URI is allowed":
check(dedent"""
[see (http://no.org)], end""".toAst ==
dedent"""
rnInner
rnLeaf '['
rnLeaf 'see'
rnLeaf ' '
rnLeaf '('
rnStandaloneHyperlink
rnLeaf 'http://no.org'
rnLeaf ')'
rnLeaf ']'
rnLeaf ','
rnLeaf ' '
rnLeaf 'end'
""")
# but `/` at the end is OK
check(
dedent"""
See http://no.org/ end""".toAst ==
dedent"""
rnInner
rnLeaf 'See'
rnLeaf ' '
rnStandaloneHyperlink
rnLeaf 'http://no.org/'
rnLeaf ' '
rnLeaf 'end'
""")
# a more complex URL with some made-up ending '&='.
# Github Markdown would include final &= and
# so would rst2html.py in contradiction with RST spec.
check(
dedent"""
See https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO&= end""".toAst ==
dedent"""
rnInner
rnLeaf 'See'
rnLeaf ' '
rnStandaloneHyperlink
rnLeaf 'https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO'
rnLeaf '&'
rnLeaf '='
rnLeaf ' '
rnLeaf 'end'
""")
test "URL with balanced parentheses (Markdown rule)":
# 2 balanced parens, 1 unbalanced:
check(dedent"""
https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst ==
dedent"""
rnInner
rnStandaloneHyperlink
rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))'
rnLeaf ')'
""")
# the same for Markdown-style link:
check(dedent"""
[foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst ==
dedent"""
rnInner
rnHyperlink
rnLeaf 'foo [bar]'
rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))'
rnLeaf ')'
""")
# unbalanced (here behavior is more RST-like actually):
check(dedent"""
https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst ==
dedent"""
rnInner
rnStandaloneHyperlink
rnLeaf 'https://en.wikipedia.org/wiki/APL_(programming_language'
rnLeaf '('
""")
# unbalanced [, but still acceptable:
check(dedent"""
[my {link example](http://example.com/bracket_(symbol_[))""".toAst ==
dedent"""
rnHyperlink
rnLeaf 'my {link example'
rnLeaf 'http://example.com/bracket_(symbol_[)'
""")