trst.nim 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997
  1. discard """
  2. output: '''
  3. [Suite] RST parsing
  4. [Suite] RST tables
  5. [Suite] RST indentation
  6. [Suite] Markdown indentation
  7. [Suite] Warnings
  8. [Suite] RST include directive
  9. [Suite] RST escaping
  10. [Suite] RST inline markup
  11. [Suite] Misc isssues
  12. '''
  13. matrix: "--mm:refc; --mm:orc"
  14. """
  15. # tests for rst module
  16. import ../../lib/packages/docutils/[rstgen, rst, rstast]
  17. import unittest, strutils
  18. import std/private/miscdollars
  19. import os
  20. import std/[assertions, syncio]
  21. const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
  22. # legacy nimforum / old default mode:
  23. const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled}
  24. const pureRst = {roNimFile, roSandboxDisabled}
  25. proc toAst(input: string,
  26. rstOptions: RstParseOptions = preferMarkdown,
  27. error: ref string = nil,
  28. warnings: ref seq[string] = nil): string =
  29. ## If `error` is nil then no errors should be generated.
  30. ## The same goes for `warnings`.
  31. result = ""
  32. proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
  33. arg: string) =
  34. let mc = msgkind.whichMsgClass
  35. let a = $msgkind % arg
  36. var message: string = ""
  37. toLocation(message, filename, line, col + ColRstOffset)
  38. message.add " $1: $2" % [$mc, a]
  39. if mc == mcError:
  40. if error == nil:
  41. raise newException(EParseError, "[unexpected error] " & message)
  42. error[] = message
  43. # we check only first error because subsequent ones may be meaningless
  44. raise newException(EParseError, "")
  45. else:
  46. doAssert warnings != nil, "unexpected RST warning '" & message & "'"
  47. warnings[].add message
  48. try:
  49. const filen = "input"
  50. proc myFindFile(filename: string): string =
  51. # we don't find any files in online mode:
  52. result = ""
  53. var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit,
  54. rstOptions, myFindFile, nil, testMsgHandler)
  55. result = treeRepr(rst)
  56. except EParseError as e:
  57. if e.msg != "":
  58. result = e.msg
  59. suite "RST parsing":
  60. test "Standalone punctuation is not parsed as heading overlines":
  61. check(dedent"""
  62. Paragraph
  63. !""".toAst ==
  64. dedent"""
  65. rnInner
  66. rnParagraph
  67. rnLeaf 'Paragraph'
  68. rnParagraph
  69. rnLeaf '!'
  70. """)
  71. check(dedent"""
  72. Paragraph1
  73. ...
  74. Paragraph2""".toAst ==
  75. dedent"""
  76. rnInner
  77. rnParagraph
  78. rnLeaf 'Paragraph1'
  79. rnParagraph
  80. rnLeaf '...'
  81. rnParagraph
  82. rnLeaf 'Paragraph2'
  83. """)
  84. check(dedent"""
  85. ---
  86. Paragraph""".toAst ==
  87. dedent"""
  88. rnInner
  89. rnLeaf '---'
  90. rnLeaf ' '
  91. rnLeaf 'Paragraph'
  92. """)
  93. test "References are whitespace-neutral and case-insensitive":
  94. # refname is 'lexical-analysis', the same for all the 3 variants:
  95. check(dedent"""
  96. Lexical Analysis
  97. ================
  98. Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_.
  99. """.toAst ==
  100. dedent"""
  101. rnInner
  102. rnHeadline level=1 anchor='lexical-analysis'
  103. rnLeaf 'Lexical'
  104. rnLeaf ' '
  105. rnLeaf 'Analysis'
  106. rnParagraph
  107. rnLeaf 'Ref'
  108. rnLeaf '.'
  109. rnLeaf ' '
  110. rnInternalRef
  111. rnInner
  112. rnLeaf 'Lexical'
  113. rnLeaf ' '
  114. rnLeaf 'Analysis'
  115. rnLeaf 'lexical-analysis'
  116. rnLeaf ' '
  117. rnLeaf 'or'
  118. rnLeaf ' '
  119. rnInternalRef
  120. rnInner
  121. rnLeaf 'Lexical'
  122. rnLeaf ' '
  123. rnLeaf 'analysis'
  124. rnLeaf 'lexical-analysis'
  125. rnLeaf ' '
  126. rnLeaf 'or'
  127. rnLeaf ' '
  128. rnInternalRef
  129. rnInner
  130. rnLeaf 'lexical'
  131. rnLeaf ' '
  132. rnLeaf 'analysis'
  133. rnLeaf 'lexical-analysis'
  134. rnLeaf '.'
  135. rnLeaf ' '
  136. """)
  137. test "RST quoted literal blocks":
  138. let expected =
  139. dedent"""
  140. rnInner
  141. rnLeaf 'Paragraph'
  142. rnLeaf ':'
  143. rnLiteralBlock
  144. rnLeaf '>x'
  145. """
  146. check(dedent"""
  147. Paragraph::
  148. >x""".toAst(rstOptions = preferRst) == expected)
  149. check(dedent"""
  150. Paragraph::
  151. >x""".toAst(rstOptions = preferRst) == expected)
  152. test "RST quoted literal blocks, :: at a separate line":
  153. let expected =
  154. dedent"""
  155. rnInner
  156. rnInner
  157. rnLeaf 'Paragraph'
  158. rnLiteralBlock
  159. rnLeaf '>x
  160. >>y'
  161. """
  162. check(dedent"""
  163. Paragraph
  164. ::
  165. >x
  166. >>y""".toAst(rstOptions = preferRst) == expected)
  167. check(dedent"""
  168. Paragraph
  169. ::
  170. >x
  171. >>y""".toAst(rstOptions = preferRst) == expected)
  172. test "Markdown quoted blocks":
  173. check(dedent"""
  174. Paragraph.
  175. >x""".toAst ==
  176. dedent"""
  177. rnInner
  178. rnLeaf 'Paragraph'
  179. rnLeaf '.'
  180. rnMarkdownBlockQuote
  181. rnMarkdownBlockQuoteItem quotationDepth=1
  182. rnLeaf 'x'
  183. """)
  184. # bug #17987
  185. check(dedent"""
  186. foo https://github.com/nim-lang/Nim/issues/8258
  187. > bar""".toAst ==
  188. dedent"""
  189. rnInner
  190. rnInner
  191. rnLeaf 'foo'
  192. rnLeaf ' '
  193. rnStandaloneHyperlink
  194. rnLeaf 'https://github.com/nim-lang/Nim/issues/8258'
  195. rnMarkdownBlockQuote
  196. rnMarkdownBlockQuoteItem quotationDepth=1
  197. rnLeaf 'bar'
  198. """)
  199. let expected = dedent"""
  200. rnInner
  201. rnLeaf 'Paragraph'
  202. rnLeaf '.'
  203. rnMarkdownBlockQuote
  204. rnMarkdownBlockQuoteItem quotationDepth=1
  205. rnInner
  206. rnLeaf 'x1'
  207. rnLeaf ' '
  208. rnLeaf 'x2'
  209. rnMarkdownBlockQuoteItem quotationDepth=2
  210. rnInner
  211. rnLeaf 'y1'
  212. rnLeaf ' '
  213. rnLeaf 'y2'
  214. rnMarkdownBlockQuoteItem quotationDepth=1
  215. rnLeaf 'z'
  216. """
  217. check(dedent"""
  218. Paragraph.
  219. >x1 x2
  220. >>y1 y2
  221. >z""".toAst == expected)
  222. check(dedent"""
  223. Paragraph.
  224. > x1 x2
  225. >> y1 y2
  226. > z""".toAst == expected)
  227. check(dedent"""
  228. >x
  229. >y
  230. >z""".toAst ==
  231. dedent"""
  232. rnMarkdownBlockQuote
  233. rnMarkdownBlockQuoteItem quotationDepth=1
  234. rnInner
  235. rnLeaf 'x'
  236. rnLeaf ' '
  237. rnLeaf 'y'
  238. rnLeaf ' '
  239. rnLeaf 'z'
  240. """)
  241. check(dedent"""
  242. > z
  243. > > >y
  244. """.toAst ==
  245. dedent"""
  246. rnMarkdownBlockQuote
  247. rnMarkdownBlockQuoteItem quotationDepth=1
  248. rnLeaf 'z'
  249. rnMarkdownBlockQuoteItem quotationDepth=3
  250. rnLeaf 'y'
  251. """)
  252. test "Markdown quoted blocks: lazy":
  253. let expected = dedent"""
  254. rnInner
  255. rnMarkdownBlockQuote
  256. rnMarkdownBlockQuoteItem quotationDepth=2
  257. rnInner
  258. rnLeaf 'x'
  259. rnLeaf ' '
  260. rnLeaf 'continuation1'
  261. rnLeaf ' '
  262. rnLeaf 'continuation2'
  263. rnParagraph
  264. rnLeaf 'newParagraph'
  265. """
  266. check(dedent"""
  267. >>x
  268. continuation1
  269. continuation2
  270. newParagraph""".toAst == expected)
  271. check(dedent"""
  272. >> x
  273. continuation1
  274. continuation2
  275. newParagraph""".toAst == expected)
  276. # however mixing more than 1 non-lazy line and lazy one(s) splits quote
  277. # in our parser, which appeared the easiest way to handle such cases:
  278. var warnings = new seq[string]
  279. check(dedent"""
  280. >> x
  281. >> continuation1
  282. continuation2
  283. newParagraph""".toAst(warnings=warnings) ==
  284. dedent"""
  285. rnInner
  286. rnMarkdownBlockQuote
  287. rnMarkdownBlockQuoteItem quotationDepth=2
  288. rnLeaf 'x'
  289. rnMarkdownBlockQuoteItem quotationDepth=2
  290. rnInner
  291. rnLeaf 'continuation1'
  292. rnLeaf ' '
  293. rnLeaf 'continuation2'
  294. rnParagraph
  295. rnLeaf 'newParagraph'
  296. """)
  297. check(warnings[] == @[
  298. "input(2, 1) Warning: RST style: two or more quoted lines " &
  299. "are followed by unquoted line 3"])
  300. test "Markdown quoted blocks: not lazy":
  301. # here is where we deviate from CommonMark specification: 'bar' below is
  302. # not considered as continuation of 2-level '>> foo' quote.
  303. check(dedent"""
  304. >>> foo
  305. > bar
  306. >> baz
  307. """.toAst() ==
  308. dedent"""
  309. rnMarkdownBlockQuote
  310. rnMarkdownBlockQuoteItem quotationDepth=3
  311. rnLeaf 'foo'
  312. rnMarkdownBlockQuoteItem quotationDepth=1
  313. rnLeaf 'bar'
  314. rnMarkdownBlockQuoteItem quotationDepth=2
  315. rnLeaf 'baz'
  316. """)
  317. test "Markdown quoted blocks: inline markup works":
  318. check(dedent"""
  319. > hi **bold** text
  320. """.toAst == dedent"""
  321. rnMarkdownBlockQuote
  322. rnMarkdownBlockQuoteItem quotationDepth=1
  323. rnInner
  324. rnLeaf 'hi'
  325. rnLeaf ' '
  326. rnStrongEmphasis
  327. rnLeaf 'bold'
  328. rnLeaf ' '
  329. rnLeaf 'text'
  330. """)
  331. test "Markdown quoted blocks: blank line separator":
  332. let expected = dedent"""
  333. rnInner
  334. rnMarkdownBlockQuote
  335. rnMarkdownBlockQuoteItem quotationDepth=1
  336. rnInner
  337. rnLeaf 'x'
  338. rnLeaf ' '
  339. rnLeaf 'y'
  340. rnMarkdownBlockQuote
  341. rnMarkdownBlockQuoteItem quotationDepth=1
  342. rnInner
  343. rnLeaf 'z'
  344. rnLeaf ' '
  345. rnLeaf 't'
  346. """
  347. check(dedent"""
  348. >x
  349. >y
  350. > z
  351. > t""".toAst == expected)
  352. check(dedent"""
  353. >x
  354. y
  355. > z
  356. t""".toAst == expected)
  357. test "Markdown quoted blocks: nested body blocks/elements work #1":
  358. let expected = dedent"""
  359. rnMarkdownBlockQuote
  360. rnMarkdownBlockQuoteItem quotationDepth=1
  361. rnBulletList
  362. rnBulletItem
  363. rnInner
  364. rnLeaf 'x'
  365. rnBulletItem
  366. rnInner
  367. rnLeaf 'y'
  368. """
  369. check(dedent"""
  370. > - x
  371. - y
  372. """.toAst == expected)
  373. # TODO: if bug #17340 point 28 is resolved then this may work:
  374. # check(dedent"""
  375. # > - x
  376. # - y
  377. # """.toAst == expected)
  378. check(dedent"""
  379. > - x
  380. > - y
  381. """.toAst == expected)
  382. check(dedent"""
  383. >
  384. > - x
  385. >
  386. > - y
  387. >
  388. """.toAst == expected)
  389. test "Markdown quoted blocks: nested body blocks/elements work #2":
  390. let expected = dedent"""
  391. rnAdmonition adType=note
  392. [nil]
  393. [nil]
  394. rnDefList
  395. rnDefItem
  396. rnDefName
  397. rnLeaf 'deflist'
  398. rnLeaf ':'
  399. rnDefBody
  400. rnMarkdownBlockQuote
  401. rnMarkdownBlockQuoteItem quotationDepth=2
  402. rnInner
  403. rnLeaf 'quote'
  404. rnLeaf ' '
  405. rnLeaf 'continuation'
  406. """
  407. check(dedent"""
  408. .. Note:: deflist:
  409. >> quote
  410. continuation
  411. """.toAst(rstOptions = preferRst) == expected)
  412. check(dedent"""
  413. .. Note::
  414. deflist:
  415. >> quote
  416. continuation
  417. """.toAst(rstOptions = preferRst) == expected)
  418. check(dedent"""
  419. .. Note::
  420. deflist:
  421. >> quote
  422. >> continuation
  423. """.toAst(rstOptions = preferRst) == expected)
  424. # spaces are not significant between `>`:
  425. check(dedent"""
  426. .. Note::
  427. deflist:
  428. > > quote
  429. > > continuation
  430. """.toAst(rstOptions = preferRst) == expected)
  431. test "Markdown quoted blocks: de-indent handled well":
  432. check(dedent"""
  433. >
  434. > - x
  435. > - y
  436. >
  437. > Paragraph.
  438. """.toAst(rstOptions = preferRst) == dedent"""
  439. rnMarkdownBlockQuote
  440. rnMarkdownBlockQuoteItem quotationDepth=1
  441. rnInner
  442. rnBlockQuote
  443. rnBulletList
  444. rnBulletItem
  445. rnInner
  446. rnLeaf 'x'
  447. rnBulletItem
  448. rnInner
  449. rnLeaf 'y'
  450. rnParagraph
  451. rnLeaf 'Paragraph'
  452. rnLeaf '.'
  453. """)
  454. let expectCodeBlock = dedent"""
  455. rnCodeBlock
  456. [nil]
  457. rnFieldList
  458. rnField
  459. rnFieldName
  460. rnLeaf 'default-language'
  461. rnFieldBody
  462. rnLeaf 'Nim'
  463. rnLiteralBlock
  464. rnLeaf 'let a = 1
  465. ```'
  466. """
  467. test "Markdown footnotes":
  468. # Testing also 1) correct order of manually-numbered and automatically-
  469. # numbered footnotes; 2) no spaces between references (html & 3 below):
  470. check(dedent"""
  471. Paragraph [^1] [^html-hyphen][^3] and [^latex]
  472. [^1]: footnote1
  473. [^html-hyphen]: footnote2
  474. continuation2
  475. [^latex]: footnote4
  476. [^3]: footnote3
  477. continuation3
  478. """.toAst ==
  479. dedent"""
  480. rnInner
  481. rnInner
  482. rnLeaf 'Paragraph'
  483. rnLeaf ' '
  484. rnFootnoteRef
  485. rnInner
  486. rnLeaf '1'
  487. rnLeaf 'footnote-1'
  488. rnLeaf ' '
  489. rnFootnoteRef
  490. rnInner
  491. rnLeaf '2'
  492. rnLeaf 'footnote-htmlminushyphen'
  493. rnFootnoteRef
  494. rnInner
  495. rnLeaf '3'
  496. rnLeaf 'footnote-3'
  497. rnLeaf ' '
  498. rnLeaf 'and'
  499. rnLeaf ' '
  500. rnFootnoteRef
  501. rnInner
  502. rnLeaf '4'
  503. rnLeaf 'footnote-latex'
  504. rnFootnoteGroup
  505. rnFootnote anchor='footnote-1'
  506. rnInner
  507. rnLeaf '1'
  508. rnLeaf 'footnote1'
  509. rnFootnote anchor='footnote-htmlminushyphen'
  510. rnInner
  511. rnLeaf '2'
  512. rnInner
  513. rnLeaf 'footnote2'
  514. rnLeaf ' '
  515. rnLeaf 'continuation2'
  516. rnFootnote anchor='footnote-latex'
  517. rnInner
  518. rnLeaf '4'
  519. rnLeaf 'footnote4'
  520. rnFootnote anchor='footnote-3'
  521. rnInner
  522. rnLeaf '3'
  523. rnInner
  524. rnLeaf 'footnote3'
  525. rnLeaf ' '
  526. rnLeaf 'continuation3'
  527. """)
  528. test "Markdown code blocks with more > 3 backticks":
  529. check(dedent"""
  530. ````
  531. let a = 1
  532. ```
  533. ````""".toAst == expectCodeBlock)
  534. test "Markdown code blocks with ~~~":
  535. check(dedent"""
  536. ~~~
  537. let a = 1
  538. ```
  539. ~~~""".toAst == expectCodeBlock)
  540. check(dedent"""
  541. ~~~~~
  542. let a = 1
  543. ```
  544. ~~~~~""".toAst == expectCodeBlock)
  545. test "Markdown code blocks with Nim-specific arguments":
  546. check(dedent"""
  547. ```nim number-lines=1 test
  548. let a = 1
  549. ```""".toAst ==
  550. dedent"""
  551. rnCodeBlock
  552. rnDirArg
  553. rnLeaf 'nim'
  554. rnFieldList
  555. rnField
  556. rnFieldName
  557. rnLeaf 'number-lines'
  558. rnFieldBody
  559. rnLeaf '1'
  560. rnField
  561. rnFieldName
  562. rnLeaf 'test'
  563. rnFieldBody
  564. rnLiteralBlock
  565. rnLeaf 'let a = 1'
  566. """)
  567. check(dedent"""
  568. ```nim test = "nim c $1" number-lines = 1
  569. let a = 1
  570. ```""".toAst ==
  571. dedent"""
  572. rnCodeBlock
  573. rnDirArg
  574. rnLeaf 'nim'
  575. rnFieldList
  576. rnField
  577. rnFieldName
  578. rnLeaf 'test'
  579. rnFieldBody
  580. rnLeaf '"nim c $1"'
  581. rnField
  582. rnFieldName
  583. rnLeaf 'number-lines'
  584. rnFieldBody
  585. rnLeaf '1'
  586. rnLiteralBlock
  587. rnLeaf 'let a = 1'
  588. """)
  589. test "additional indentation < 4 spaces is handled fine":
  590. check(dedent"""
  591. Indentation
  592. ```nim
  593. let a = 1
  594. ```""".toAst ==
  595. dedent"""
  596. rnInner
  597. rnParagraph
  598. rnLeaf 'Indentation'
  599. rnParagraph
  600. rnCodeBlock
  601. rnDirArg
  602. rnLeaf 'nim'
  603. [nil]
  604. rnLiteralBlock
  605. rnLeaf ' let a = 1'
  606. """)
  607. # | |
  608. # | \ indentation of exactly two spaces before 'let a = 1'
  609. test "no blank line is required before or after Markdown code block":
  610. let inputBacktick = dedent"""
  611. Some text
  612. ```
  613. CodeBlock()
  614. ```
  615. Other text"""
  616. let inputTilde = dedent"""
  617. Some text
  618. ~~~~~~~~~
  619. CodeBlock()
  620. ~~~~~~~~~
  621. Other text"""
  622. let expected = dedent"""
  623. rnInner
  624. rnParagraph
  625. rnLeaf 'Some'
  626. rnLeaf ' '
  627. rnLeaf 'text'
  628. rnParagraph
  629. rnCodeBlock
  630. [nil]
  631. rnFieldList
  632. rnField
  633. rnFieldName
  634. rnLeaf 'default-language'
  635. rnFieldBody
  636. rnLeaf 'Nim'
  637. rnLiteralBlock
  638. rnLeaf 'CodeBlock()'
  639. rnLeaf ' '
  640. rnLeaf 'Other'
  641. rnLeaf ' '
  642. rnLeaf 'text'
  643. """
  644. check inputBacktick.toAst == expected
  645. check inputTilde.toAst == expected
  646. test "option list has priority over definition list":
  647. for opt in [preferMarkdown, preferRst]:
  648. check(dedent"""
  649. --defusages
  650. file
  651. -o set
  652. """.toAst(rstOptions = opt) ==
  653. dedent"""
  654. rnOptionList
  655. rnOptionListItem order=1
  656. rnOptionGroup
  657. rnLeaf '--'
  658. rnLeaf 'defusages'
  659. rnDescription
  660. rnInner
  661. rnLeaf 'file'
  662. rnOptionListItem order=2
  663. rnOptionGroup
  664. rnLeaf '-'
  665. rnLeaf 'o'
  666. rnDescription
  667. rnLeaf 'set'
  668. """)
  669. test "items of 1 option list can be separated by blank lines":
  670. check(dedent"""
  671. -a desc1
  672. -b desc2
  673. """.toAst ==
  674. dedent"""
  675. rnOptionList
  676. rnOptionListItem order=1
  677. rnOptionGroup
  678. rnLeaf '-'
  679. rnLeaf 'a'
  680. rnDescription
  681. rnLeaf 'desc1'
  682. rnOptionListItem order=2
  683. rnOptionGroup
  684. rnLeaf '-'
  685. rnLeaf 'b'
  686. rnDescription
  687. rnLeaf 'desc2'
  688. """)
  689. test "definition list does not gobble up the following blocks":
  690. check(dedent"""
  691. defName
  692. defBody
  693. -b desc2
  694. """.toAst(rstOptions = preferRst) ==
  695. dedent"""
  696. rnInner
  697. rnDefList
  698. rnDefItem
  699. rnDefName
  700. rnLeaf 'defName'
  701. rnDefBody
  702. rnInner
  703. rnLeaf 'defBody'
  704. rnOptionList
  705. rnOptionListItem order=1
  706. rnOptionGroup
  707. rnLeaf '-'
  708. rnLeaf 'b'
  709. rnDescription
  710. rnLeaf 'desc2'
  711. """)
  712. test "definition lists work correctly with additional indentation in Markdown":
  713. check(dedent"""
  714. Paragraph:
  715. -c desc1
  716. -b desc2
  717. """.toAst() ==
  718. dedent"""
  719. rnInner
  720. rnInner
  721. rnLeaf 'Paragraph'
  722. rnLeaf ':'
  723. rnOptionList
  724. rnOptionListItem order=1
  725. rnOptionGroup
  726. rnLeaf '-'
  727. rnLeaf 'c'
  728. rnDescription
  729. rnLeaf 'desc1'
  730. rnOptionListItem order=2
  731. rnOptionGroup
  732. rnLeaf '-'
  733. rnLeaf 'b'
  734. rnDescription
  735. rnLeaf 'desc2'
  736. """)
  737. test "RST comment":
  738. check(dedent"""
  739. .. comment1
  740. comment2
  741. someParagraph""".toAst ==
  742. dedent"""
  743. rnLeaf 'someParagraph'
  744. """)
  745. check(dedent"""
  746. ..
  747. comment1
  748. comment2
  749. someParagraph""".toAst ==
  750. dedent"""
  751. rnLeaf 'someParagraph'
  752. """)
  753. test "check that additional line right after .. ends comment":
  754. check(dedent"""
  755. ..
  756. notAcomment1
  757. notAcomment2
  758. someParagraph""".toAst(rstOptions = preferRst) ==
  759. dedent"""
  760. rnInner
  761. rnBlockQuote
  762. rnInner
  763. rnLeaf 'notAcomment1'
  764. rnLeaf ' '
  765. rnLeaf 'notAcomment2'
  766. rnParagraph
  767. rnLeaf 'someParagraph'
  768. """)
  769. test "check that additional line right after .. ends comment (Markdown mode)":
  770. # in Markdown small indentation does not matter so this should
  771. # just be split to 2 paragraphs.
  772. check(dedent"""
  773. ..
  774. notAcomment1
  775. notAcomment2
  776. someParagraph""".toAst ==
  777. dedent"""
  778. rnInner
  779. rnInner
  780. rnLeaf 'notAcomment1'
  781. rnLeaf ' '
  782. rnLeaf 'notAcomment2'
  783. rnParagraph
  784. rnLeaf 'someParagraph'
  785. """)
  786. test "but blank lines after 2nd non-empty line don't end the comment":
  787. check(dedent"""
  788. ..
  789. comment1
  790. comment2
  791. someParagraph""".toAst ==
  792. dedent"""
  793. rnLeaf 'someParagraph'
  794. """)
  795. test "using .. as separator b/w directives and block quotes":
  796. check(dedent"""
  797. .. note:: someNote
  798. ..
  799. someBlockQuote""".toAst(rstOptions = preferRst) ==
  800. dedent"""
  801. rnInner
  802. rnAdmonition adType=note
  803. [nil]
  804. [nil]
  805. rnLeaf 'someNote'
  806. rnBlockQuote
  807. rnInner
  808. rnLeaf 'someBlockQuote'
  809. """)
  810. test "no redundant blank lines in literal blocks":
  811. check(dedent"""
  812. Check::
  813. code
  814. """.toAst(rstOptions = preferRst) ==
  815. dedent"""
  816. rnInner
  817. rnLeaf 'Check'
  818. rnLeaf ':'
  819. rnLiteralBlock
  820. rnLeaf 'code'
  821. """)
  822. test "Markdown indented code blocks":
  823. check(dedent"""
  824. See
  825. some code""".toAst ==
  826. dedent"""
  827. rnInner
  828. rnInner
  829. rnLeaf 'See'
  830. rnLiteralBlock
  831. rnLeaf 'some code'
  832. """)
  833. # not a code block -- no blank line before:
  834. check(dedent"""
  835. See
  836. some code""".toAst ==
  837. dedent"""
  838. rnInner
  839. rnLeaf 'See'
  840. rnLeaf ' '
  841. rnLeaf 'some'
  842. rnLeaf ' '
  843. rnLeaf 'code'
  844. """)
  845. suite "RST tables":
  846. test "formatting in tables works":
  847. check(
  848. dedent"""
  849. ========= ===
  850. `build` `a`
  851. ========= ===
  852. """.toAst ==
  853. dedent"""
  854. rnTable colCount=2
  855. rnTableRow
  856. rnTableDataCell
  857. rnInlineCode
  858. rnDirArg
  859. rnLeaf 'nim'
  860. [nil]
  861. rnLiteralBlock
  862. rnLeaf 'build'
  863. rnTableDataCell
  864. rnInlineCode
  865. rnDirArg
  866. rnLeaf 'nim'
  867. [nil]
  868. rnLiteralBlock
  869. rnLeaf 'a'
  870. """)
  871. test "tables with slightly overflowed cells cause an error (1)":
  872. var error = new string
  873. check(
  874. dedent"""
  875. ====== ======
  876. Inputs Output
  877. ====== ======
  878. """.toAst(rstOptions = pureRst, error = error) == "")
  879. check(error[] == "input(2, 2) Error: Illformed table: " &
  880. "this word crosses table column from the right")
  881. # In nimforum compatibility mode & Markdown we raise a warning instead:
  882. let expected = dedent"""
  883. rnTable colCount=2
  884. rnTableRow
  885. rnTableDataCell
  886. rnLeaf 'Inputs'
  887. rnTableDataCell
  888. rnLeaf 'Output'
  889. """
  890. for opt in [preferRst, preferMarkdown]:
  891. var warnings = new seq[string]
  892. check(
  893. dedent"""
  894. ====== ======
  895. Inputs Output
  896. ====== ======
  897. """.toAst(rstOptions = opt, warnings = warnings) == expected)
  898. check(warnings[] == @[
  899. "input(2, 2) Warning: RST style: this word crosses table column from the right"])
  900. test "tables with slightly overflowed cells cause an error (2)":
  901. var error = new string
  902. check("" == dedent"""
  903. ===== ===== ======
  904. Input Output
  905. ===== ===== ======
  906. False False False
  907. ===== ===== ======
  908. """.toAst(rstOptions = pureRst, error = error))
  909. check(error[] == "input(2, 8) Error: Illformed table: " &
  910. "this word crosses table column from the right")
  911. test "tables with slightly underflowed cells cause an error":
  912. var error = new string
  913. check("" == dedent"""
  914. ===== ===== ======
  915. Input Output
  916. ===== ===== ======
  917. False False False
  918. ===== ===== ======
  919. """.toAst(rstOptions = pureRst, error = error))
  920. check(error[] == "input(2, 7) Error: Illformed table: " &
  921. "this word crosses table column from the left")
  922. test "tables with unequal underlines should be reported (1)":
  923. var error = new string
  924. error[] = "none"
  925. check("" == dedent"""
  926. ===== ======
  927. Input Output
  928. ===== ======
  929. False False
  930. ===== =======
  931. """.toAst(rstOptions = pureRst, error = error))
  932. check(error[] == "input(5, 14) Error: Illformed table: " &
  933. "end of table column #2 should end at position 13")
  934. test "tables with unequal underlines should be reported (2)":
  935. var error = new string
  936. check("" == dedent"""
  937. ===== ======
  938. Input Output
  939. ===== =======
  940. False False
  941. ===== ======
  942. """.toAst(rstOptions = pureRst, error = error))
  943. check(error[] == "input(3, 14) Error: Illformed table: " &
  944. "end of table column #2 should end at position 13")
  945. test "tables with empty first cells":
  946. check(
  947. dedent"""
  948. = = =
  949. x y z
  950. t
  951. = = =
  952. """.toAst ==
  953. dedent"""
  954. rnTable colCount=3
  955. rnTableRow
  956. rnTableDataCell
  957. rnLeaf 'x'
  958. rnTableDataCell
  959. rnInner
  960. rnLeaf 'y'
  961. rnLeaf ' '
  962. rnTableDataCell
  963. rnInner
  964. rnLeaf 'z'
  965. rnLeaf ' '
  966. rnLeaf 't'
  967. """)
  968. test "tables with spanning cells & separators":
  969. check(
  970. dedent"""
  971. ===== ===== ======
  972. Inputs Output
  973. ------------ ------
  974. A B A or B
  975. ===== ===== ======
  976. False False False
  977. True False True
  978. ----- ----- ------
  979. False True True
  980. True True True
  981. ===== ===== ======
  982. """.toAst ==
  983. dedent"""
  984. rnTable colCount=3
  985. rnTableRow
  986. rnTableHeaderCell span=2
  987. rnLeaf 'Inputs'
  988. rnTableHeaderCell span=1
  989. rnLeaf 'Output'
  990. rnTableRow endsHeader
  991. rnTableHeaderCell
  992. rnLeaf 'A'
  993. rnTableHeaderCell
  994. rnLeaf 'B'
  995. rnTableHeaderCell
  996. rnInner
  997. rnLeaf 'A'
  998. rnLeaf ' '
  999. rnLeaf 'or'
  1000. rnLeaf ' '
  1001. rnLeaf 'B'
  1002. rnTableRow
  1003. rnTableDataCell
  1004. rnLeaf 'False'
  1005. rnTableDataCell
  1006. rnLeaf 'False'
  1007. rnTableDataCell
  1008. rnLeaf 'False'
  1009. rnTableRow
  1010. rnTableDataCell span=1
  1011. rnLeaf 'True'
  1012. rnTableDataCell span=1
  1013. rnLeaf 'False'
  1014. rnTableDataCell span=1
  1015. rnLeaf 'True'
  1016. rnTableRow
  1017. rnTableDataCell
  1018. rnLeaf 'False'
  1019. rnTableDataCell
  1020. rnLeaf 'True'
  1021. rnTableDataCell
  1022. rnLeaf 'True'
  1023. rnTableRow
  1024. rnTableDataCell
  1025. rnLeaf 'True'
  1026. rnTableDataCell
  1027. rnLeaf 'True'
  1028. rnTableDataCell
  1029. rnLeaf 'True'
  1030. """)
  1031. test "tables with spanning cells with uneqal underlines cause an error":
  1032. var error = new string
  1033. check(
  1034. dedent"""
  1035. ===== ===== ======
  1036. Inputs Output
  1037. ------------- ------
  1038. A B A or B
  1039. ===== ===== ======
  1040. """.toAst(error=error) == "")
  1041. check(error[] == "input(3, 1) Error: Illformed table: " &
  1042. "spanning underline does not match main table columns")
  1043. let expTable = dedent"""
  1044. rnTable colCount=2
  1045. rnTableRow
  1046. rnTableDataCell
  1047. rnLeaf 'Inputs'
  1048. rnTableDataCell
  1049. rnLeaf 'Output'
  1050. """
  1051. test "only tables with `=` columns specs are allowed (1)":
  1052. var warnings = new seq[string]
  1053. check(
  1054. dedent"""
  1055. ------ ------
  1056. Inputs Output
  1057. ------ ------
  1058. """.toAst(warnings=warnings) ==
  1059. expTable)
  1060. check(warnings[] ==
  1061. @["input(1, 1) Warning: RST style: " &
  1062. "only tables with `=` columns specification are allowed",
  1063. "input(3, 1) Warning: RST style: " &
  1064. "only tables with `=` columns specification are allowed"])
  1065. test "only tables with `=` columns specs are allowed (2)":
  1066. var warnings = new seq[string]
  1067. check(
  1068. dedent"""
  1069. ====== ======
  1070. Inputs Output
  1071. ~~~~~~ ~~~~~~
  1072. """.toAst(warnings=warnings) ==
  1073. expTable)
  1074. check(warnings[] ==
  1075. @["input(3, 1) Warning: RST style: "&
  1076. "only tables with `=` columns specification are allowed"])
  1077. suite "RST indentation":
  1078. test "nested bullet lists":
  1079. let input = dedent """
  1080. * - bullet1
  1081. - bullet2
  1082. * - bullet3
  1083. - bullet4
  1084. """
  1085. let output = input.toAst
  1086. check(output == dedent"""
  1087. rnBulletList
  1088. rnBulletItem
  1089. rnBulletList
  1090. rnBulletItem
  1091. rnInner
  1092. rnLeaf 'bullet1'
  1093. rnBulletItem
  1094. rnInner
  1095. rnLeaf 'bullet2'
  1096. rnBulletItem
  1097. rnBulletList
  1098. rnBulletItem
  1099. rnInner
  1100. rnLeaf 'bullet3'
  1101. rnBulletItem
  1102. rnInner
  1103. rnLeaf 'bullet4'
  1104. """)
  1105. test "nested markup blocks":
  1106. let input = dedent"""
  1107. #) .. Hint:: .. Error:: none
  1108. #) .. Warning:: term0
  1109. Definition0
  1110. #) some
  1111. paragraph1
  1112. #) term1
  1113. Definition1
  1114. term2
  1115. Definition2
  1116. """
  1117. check(input.toAst(rstOptions = preferRst) == dedent"""
  1118. rnEnumList labelFmt=1)
  1119. rnEnumItem
  1120. rnAdmonition adType=hint
  1121. [nil]
  1122. [nil]
  1123. rnAdmonition adType=error
  1124. [nil]
  1125. [nil]
  1126. rnLeaf 'none'
  1127. rnEnumItem
  1128. rnAdmonition adType=warning
  1129. [nil]
  1130. [nil]
  1131. rnDefList
  1132. rnDefItem
  1133. rnDefName
  1134. rnLeaf 'term0'
  1135. rnDefBody
  1136. rnInner
  1137. rnLeaf 'Definition0'
  1138. rnEnumItem
  1139. rnInner
  1140. rnLeaf 'some'
  1141. rnLeaf ' '
  1142. rnLeaf 'paragraph1'
  1143. rnEnumItem
  1144. rnDefList
  1145. rnDefItem
  1146. rnDefName
  1147. rnLeaf 'term1'
  1148. rnDefBody
  1149. rnInner
  1150. rnLeaf 'Definition1'
  1151. rnDefItem
  1152. rnDefName
  1153. rnLeaf 'term2'
  1154. rnDefBody
  1155. rnInner
  1156. rnLeaf 'Definition2'
  1157. """)
  1158. test "code-block parsing":
  1159. let input1 = dedent"""
  1160. .. code-block:: nim
  1161. :test: "nim c $1"
  1162. template additive(typ: typedesc) =
  1163. discard
  1164. """
  1165. let input2 = dedent"""
  1166. .. code-block:: nim
  1167. :test: "nim c $1"
  1168. template additive(typ: typedesc) =
  1169. discard
  1170. """
  1171. let input3 = dedent"""
  1172. .. code-block:: nim
  1173. :test: "nim c $1"
  1174. template additive(typ: typedesc) =
  1175. discard
  1176. """
  1177. let inputWrong = dedent"""
  1178. .. code-block:: nim
  1179. :test: "nim c $1"
  1180. template additive(typ: typedesc) =
  1181. discard
  1182. """
  1183. let ast = dedent"""
  1184. rnCodeBlock
  1185. rnDirArg
  1186. rnLeaf 'nim'
  1187. rnFieldList
  1188. rnField
  1189. rnFieldName
  1190. rnLeaf 'test'
  1191. rnFieldBody
  1192. rnInner
  1193. rnLeaf '"'
  1194. rnLeaf 'nim'
  1195. rnLeaf ' '
  1196. rnLeaf 'c'
  1197. rnLeaf ' '
  1198. rnLeaf '$'
  1199. rnLeaf '1'
  1200. rnLeaf '"'
  1201. rnField
  1202. rnFieldName
  1203. rnLeaf 'default-language'
  1204. rnFieldBody
  1205. rnLeaf 'Nim'
  1206. rnLiteralBlock
  1207. rnLeaf 'template additive(typ: typedesc) =
  1208. discard'
  1209. """
  1210. check input1.toAst == ast
  1211. check input2.toAst == ast
  1212. check input3.toAst == ast
  1213. # "template..." should be parsed as a definition list attached to ":test:":
  1214. check inputWrong.toAst != ast
  1215. test "Markdown definition lists work in conjunction with bullet lists":
  1216. check(dedent"""
  1217. * some term
  1218. : the definition
  1219. Paragraph.""".toAst ==
  1220. dedent"""
  1221. rnInner
  1222. rnBulletList
  1223. rnBulletItem
  1224. rnMdDefList
  1225. rnDefItem
  1226. rnDefName
  1227. rnLeaf 'some'
  1228. rnLeaf ' '
  1229. rnLeaf 'term'
  1230. rnDefBody
  1231. rnInner
  1232. rnLeaf 'the'
  1233. rnLeaf ' '
  1234. rnLeaf 'definition'
  1235. rnParagraph
  1236. rnLeaf 'Paragraph'
  1237. rnLeaf '.'
  1238. """)
  1239. test "Markdown definition lists work with blank lines and extra paragraphs":
  1240. check(dedent"""
  1241. Term1
  1242. : Definition1
  1243. Term2 *inline markup*
  1244. : Definition2
  1245. Paragraph2
  1246. Term3
  1247. : * point1
  1248. * point2
  1249. : term3definition2
  1250. """.toAst == dedent"""
  1251. rnMdDefList
  1252. rnDefItem
  1253. rnDefName
  1254. rnLeaf 'Term1'
  1255. rnDefBody
  1256. rnInner
  1257. rnLeaf 'Definition1'
  1258. rnDefItem
  1259. rnDefName
  1260. rnLeaf 'Term2'
  1261. rnLeaf ' '
  1262. rnEmphasis
  1263. rnLeaf 'inline'
  1264. rnLeaf ' '
  1265. rnLeaf 'markup'
  1266. rnDefBody
  1267. rnParagraph
  1268. rnLeaf 'Definition2'
  1269. rnParagraph
  1270. rnLeaf 'Paragraph2'
  1271. rnDefItem
  1272. rnDefName
  1273. rnLeaf 'Term3'
  1274. rnDefBody
  1275. rnBulletList
  1276. rnBulletItem
  1277. rnInner
  1278. rnLeaf 'point1'
  1279. rnBulletItem
  1280. rnInner
  1281. rnLeaf 'point2'
  1282. rnDefBody
  1283. rnInner
  1284. rnLeaf 'term3definition2'
  1285. """)
  1286. suite "Markdown indentation":
  1287. test "Markdown paragraph indentation":
  1288. # Additional spaces (<=3) of indentation does not break the paragraph.
  1289. # TODO: in 2nd case de-indentation causes paragraph to break, this is
  1290. # reasonable but does not seem to conform the Markdown spec.
  1291. check(dedent"""
  1292. Start1
  1293. stop1
  1294. Start2
  1295. stop2
  1296. """.toAst ==
  1297. dedent"""
  1298. rnInner
  1299. rnParagraph
  1300. rnLeaf 'Start1'
  1301. rnLeaf ' '
  1302. rnLeaf 'stop1'
  1303. rnParagraph
  1304. rnLeaf 'Start2'
  1305. rnParagraph
  1306. rnLeaf 'stop2'
  1307. rnLeaf ' '
  1308. """)
  1309. suite "Warnings":
  1310. test "warnings for broken footnotes/links/substitutions":
  1311. let input = dedent"""
  1312. firstParagraph
  1313. footnoteRef [som]_
  1314. link `a broken Link`_
  1315. substitution |undefined subst|
  1316. link short.link_
  1317. lastParagraph
  1318. """
  1319. var warnings = new seq[string]
  1320. let output = input.toAst(rstOptions=preferRst, warnings=warnings)
  1321. check(warnings[] == @[
  1322. "input(3, 14) Warning: broken link 'citation-som'",
  1323. "input(5, 7) Warning: broken link 'a broken Link'",
  1324. "input(7, 15) Warning: unknown substitution 'undefined subst'",
  1325. "input(9, 6) Warning: broken link 'short.link'"
  1326. ])
  1327. test "Pandoc Markdown concise link warning points to target":
  1328. var warnings = new seq[string]
  1329. check(
  1330. "ref [here][target]".toAst(warnings=warnings) ==
  1331. dedent"""
  1332. rnInner
  1333. rnLeaf 'ref'
  1334. rnLeaf ' '
  1335. rnPandocRef
  1336. rnInner
  1337. rnLeaf 'here'
  1338. rnInner
  1339. rnLeaf 'target'
  1340. """)
  1341. check warnings[] == @["input(1, 12) Warning: broken link 'target'"]
  1342. test "With include directive and blank lines at the beginning":
  1343. "other.rst".writeFile(dedent"""
  1344. firstParagraph
  1345. here brokenLink_""")
  1346. let input = ".. include:: other.rst"
  1347. var warnings = new seq[string]
  1348. let output = input.toAst(warnings=warnings)
  1349. check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"]
  1350. check(output == dedent"""
  1351. rnInner
  1352. rnParagraph
  1353. rnLeaf 'firstParagraph'
  1354. rnParagraph
  1355. rnLeaf 'here'
  1356. rnLeaf ' '
  1357. rnRstRef
  1358. rnLeaf 'brokenLink'
  1359. """)
  1360. removeFile("other.rst")
  1361. test "warnings for ambiguous links (references + anchors)":
  1362. # Reference like `x`_ generates a link alias x that may clash with others
  1363. let input = dedent"""
  1364. Manual reference: `foo <#foo,string,string>`_
  1365. .. _foo:
  1366. Paragraph.
  1367. Ref foo_
  1368. """
  1369. var warnings = new seq[string]
  1370. let output = input.toAst(warnings=warnings)
  1371. check(warnings[] == @[
  1372. dedent """
  1373. input(7, 5) Warning: ambiguous doc link `foo`
  1374. clash:
  1375. (3, 8): (manual directive anchor)
  1376. (1, 45): (implicitly-generated hyperlink alias)"""
  1377. ])
  1378. # reference should be resolved to the manually set anchor:
  1379. check(output ==
  1380. dedent"""
  1381. rnInner
  1382. rnParagraph
  1383. rnLeaf 'Manual'
  1384. rnLeaf ' '
  1385. rnLeaf 'reference'
  1386. rnLeaf ':'
  1387. rnLeaf ' '
  1388. rnHyperlink
  1389. rnInner
  1390. rnLeaf 'foo'
  1391. rnInner
  1392. rnLeaf '#foo,string,string'
  1393. rnParagraph anchor='foo'
  1394. rnLeaf 'Paragraph'
  1395. rnLeaf '.'
  1396. rnParagraph
  1397. rnLeaf 'Ref'
  1398. rnLeaf ' '
  1399. rnInternalRef
  1400. rnInner
  1401. rnLeaf 'foo'
  1402. rnLeaf 'foo'
  1403. rnLeaf ' '
  1404. """)
  1405. suite "RST include directive":
  1406. test "Include whole":
  1407. "other.rst".writeFile("**test1**")
  1408. let input = ".. include:: other.rst"
  1409. doAssert "<strong>test1</strong>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
  1410. removeFile("other.rst")
  1411. test "Include starting from":
  1412. "other.rst".writeFile("""
  1413. And this should **NOT** be visible in `docs.html`
  1414. OtherStart
  1415. *Visible*
  1416. """)
  1417. let input = """
  1418. .. include:: other.rst
  1419. :start-after: OtherStart
  1420. """
  1421. check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
  1422. removeFile("other.rst")
  1423. test "Include everything before":
  1424. "other.rst".writeFile("""
  1425. *Visible*
  1426. OtherEnd
  1427. And this should **NOT** be visible in `docs.html`
  1428. """)
  1429. let input = """
  1430. .. include:: other.rst
  1431. :end-before: OtherEnd
  1432. """
  1433. doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
  1434. removeFile("other.rst")
  1435. test "Include everything between":
  1436. "other.rst".writeFile("""
  1437. And this should **NOT** be visible in `docs.html`
  1438. OtherStart
  1439. *Visible*
  1440. OtherEnd
  1441. And this should **NOT** be visible in `docs.html`
  1442. """)
  1443. let input = """
  1444. .. include:: other.rst
  1445. :start-after: OtherStart
  1446. :end-before: OtherEnd
  1447. """
  1448. check "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
  1449. removeFile("other.rst")
  1450. test "Ignore premature ending string":
  1451. "other.rst".writeFile("""
  1452. OtherEnd
  1453. And this should **NOT** be visible in `docs.html`
  1454. OtherStart
  1455. *Visible*
  1456. OtherEnd
  1457. And this should **NOT** be visible in `docs.html`
  1458. """)
  1459. let input = """
  1460. .. include:: other.rst
  1461. :start-after: OtherStart
  1462. :end-before: OtherEnd
  1463. """
  1464. doAssert "<em>Visible</em>" == rstToHtml(input, {roSandboxDisabled}, defaultConfig())
  1465. removeFile("other.rst")
  1466. suite "RST escaping":
  1467. test "backspaces":
  1468. check("""\ this""".toAst == dedent"""
  1469. rnLeaf 'this'
  1470. """)
  1471. check("""\\ this""".toAst == dedent"""
  1472. rnInner
  1473. rnLeaf '\'
  1474. rnLeaf ' '
  1475. rnLeaf 'this'
  1476. """)
  1477. check("""\\\ this""".toAst == dedent"""
  1478. rnInner
  1479. rnLeaf '\'
  1480. rnLeaf 'this'
  1481. """)
  1482. check("""\\\\ this""".toAst == dedent"""
  1483. rnInner
  1484. rnLeaf '\'
  1485. rnLeaf '\'
  1486. rnLeaf ' '
  1487. rnLeaf 'this'
  1488. """)
  1489. suite "RST inline markup":
  1490. test "* and ** surrounded by spaces are not inline markup":
  1491. check("a * b * c ** d ** e".toAst == dedent"""
  1492. rnInner
  1493. rnLeaf 'a'
  1494. rnLeaf ' '
  1495. rnLeaf '*'
  1496. rnLeaf ' '
  1497. rnLeaf 'b'
  1498. rnLeaf ' '
  1499. rnLeaf '*'
  1500. rnLeaf ' '
  1501. rnLeaf 'c'
  1502. rnLeaf ' '
  1503. rnLeaf '**'
  1504. rnLeaf ' '
  1505. rnLeaf 'd'
  1506. rnLeaf ' '
  1507. rnLeaf '**'
  1508. rnLeaf ' '
  1509. rnLeaf 'e'
  1510. """)
  1511. test "end-string has repeating symbols":
  1512. check("*emphasis content****".toAst == dedent"""
  1513. rnEmphasis
  1514. rnLeaf 'emphasis'
  1515. rnLeaf ' '
  1516. rnLeaf 'content'
  1517. rnLeaf '***'
  1518. """)
  1519. check("""*emphasis content\****""".toAst == dedent"""
  1520. rnEmphasis
  1521. rnLeaf 'emphasis'
  1522. rnLeaf ' '
  1523. rnLeaf 'content'
  1524. rnLeaf '*'
  1525. rnLeaf '**'
  1526. """) # exact configuration of leafs with * is not really essential,
  1527. # only total number of * is essential
  1528. check("**strong content****".toAst == dedent"""
  1529. rnStrongEmphasis
  1530. rnLeaf 'strong'
  1531. rnLeaf ' '
  1532. rnLeaf 'content'
  1533. rnLeaf '**'
  1534. """)
  1535. check("""**strong content*\****""".toAst == dedent"""
  1536. rnStrongEmphasis
  1537. rnLeaf 'strong'
  1538. rnLeaf ' '
  1539. rnLeaf 'content'
  1540. rnLeaf '*'
  1541. rnLeaf '*'
  1542. rnLeaf '*'
  1543. """)
  1544. check("``lit content`````".toAst == dedent"""
  1545. rnInlineLiteral
  1546. rnLeaf 'lit'
  1547. rnLeaf ' '
  1548. rnLeaf 'content'
  1549. rnLeaf '```'
  1550. """)
  1551. test "interpreted text parsing: code fragments":
  1552. check(dedent"""
  1553. .. default-role:: option
  1554. `--gc:refc`""".toAst ==
  1555. dedent"""
  1556. rnInner
  1557. rnDefaultRole
  1558. rnDirArg
  1559. rnLeaf 'option'
  1560. [nil]
  1561. [nil]
  1562. rnParagraph
  1563. rnCodeFragment
  1564. rnInner
  1565. rnLeaf '--'
  1566. rnLeaf 'gc'
  1567. rnLeaf ':'
  1568. rnLeaf 'refc'
  1569. rnLeaf 'option'
  1570. """)
  1571. test """interpreted text can be ended with \` """:
  1572. let output = (".. default-role:: literal\n" & """`\``""").toAst
  1573. check(output.endsWith """
  1574. rnParagraph
  1575. rnInlineLiteral
  1576. rnLeaf '`'""" & "\n")
  1577. let output2 = """`\``""".toAst
  1578. check(output2 == dedent"""
  1579. rnInlineCode
  1580. rnDirArg
  1581. rnLeaf 'nim'
  1582. [nil]
  1583. rnLiteralBlock
  1584. rnLeaf '`'
  1585. """)
  1586. let output3 = """`proc \`+\``""".toAst
  1587. check(output3 == dedent"""
  1588. rnInlineCode
  1589. rnDirArg
  1590. rnLeaf 'nim'
  1591. [nil]
  1592. rnLiteralBlock
  1593. rnLeaf 'proc `+`'
  1594. """)
  1595. check("""`\\`""".toAst ==
  1596. dedent"""
  1597. rnInlineCode
  1598. rnDirArg
  1599. rnLeaf 'nim'
  1600. [nil]
  1601. rnLiteralBlock
  1602. rnLeaf '\\'
  1603. """)
  1604. test "Markdown-style code/backtick":
  1605. # no whitespace is required before `
  1606. check("`try`...`except`".toAst ==
  1607. dedent"""
  1608. rnInner
  1609. rnInlineCode
  1610. rnDirArg
  1611. rnLeaf 'nim'
  1612. [nil]
  1613. rnLiteralBlock
  1614. rnLeaf 'try'
  1615. rnLeaf '...'
  1616. rnInlineCode
  1617. rnDirArg
  1618. rnLeaf 'nim'
  1619. [nil]
  1620. rnLiteralBlock
  1621. rnLeaf 'except'
  1622. """)
  1623. test """inline literals can contain \ anywhere""":
  1624. check("""``\``""".toAst == dedent"""
  1625. rnInlineLiteral
  1626. rnLeaf '\'
  1627. """)
  1628. check("""``\\``""".toAst == dedent"""
  1629. rnInlineLiteral
  1630. rnLeaf '\'
  1631. rnLeaf '\'
  1632. """)
  1633. check("""``\```""".toAst == dedent"""
  1634. rnInlineLiteral
  1635. rnLeaf '\'
  1636. rnLeaf '`'
  1637. """)
  1638. check("""``\\```""".toAst == dedent"""
  1639. rnInlineLiteral
  1640. rnLeaf '\'
  1641. rnLeaf '\'
  1642. rnLeaf '`'
  1643. """)
  1644. check("""``\````""".toAst == dedent"""
  1645. rnInlineLiteral
  1646. rnLeaf '\'
  1647. rnLeaf '`'
  1648. rnLeaf '`'
  1649. """)
  1650. test "references with _ at the end":
  1651. check(dedent"""
  1652. .. _lnk: https
  1653. lnk_""".toAst ==
  1654. dedent"""
  1655. rnHyperlink
  1656. rnInner
  1657. rnLeaf 'lnk'
  1658. rnInner
  1659. rnLeaf 'https'
  1660. """)
  1661. test "not a hyper link":
  1662. check(dedent"""
  1663. .. _lnk: https
  1664. lnk___""".toAst ==
  1665. dedent"""
  1666. rnInner
  1667. rnLeaf 'lnk'
  1668. rnLeaf '___'
  1669. """)
  1670. test "no punctuation in the end of a standalone URI is allowed":
  1671. check(dedent"""
  1672. [see (http://no.org)], end""".toAst(rstOptions = preferRst) ==
  1673. dedent"""
  1674. rnInner
  1675. rnLeaf '['
  1676. rnLeaf 'see'
  1677. rnLeaf ' '
  1678. rnLeaf '('
  1679. rnStandaloneHyperlink
  1680. rnLeaf 'http://no.org'
  1681. rnLeaf ')'
  1682. rnLeaf ']'
  1683. rnLeaf ','
  1684. rnLeaf ' '
  1685. rnLeaf 'end'
  1686. """)
  1687. # but `/` at the end is OK
  1688. check(
  1689. dedent"""
  1690. See http://no.org/ end""".toAst ==
  1691. dedent"""
  1692. rnInner
  1693. rnLeaf 'See'
  1694. rnLeaf ' '
  1695. rnStandaloneHyperlink
  1696. rnLeaf 'http://no.org/'
  1697. rnLeaf ' '
  1698. rnLeaf 'end'
  1699. """)
  1700. # a more complex URL with some made-up ending '&='.
  1701. # Github Markdown would include final &= and
  1702. # so would rst2html.py in contradiction with RST spec.
  1703. check(
  1704. dedent"""
  1705. 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 ==
  1706. dedent"""
  1707. rnInner
  1708. rnLeaf 'See'
  1709. rnLeaf ' '
  1710. rnStandaloneHyperlink
  1711. 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'
  1712. rnLeaf '&'
  1713. rnLeaf '='
  1714. rnLeaf ' '
  1715. rnLeaf 'end'
  1716. """)
  1717. test "Markdown-style link can be split to a few lines":
  1718. check(dedent"""
  1719. is [term-rewriting
  1720. macros](manual.html#term-rewriting-macros)""".toAst ==
  1721. dedent"""
  1722. rnInner
  1723. rnLeaf 'is'
  1724. rnLeaf ' '
  1725. rnHyperlink
  1726. rnLeaf 'term-rewriting macros'
  1727. rnLeaf 'manual.html#term-rewriting-macros'
  1728. """)
  1729. test "URL with balanced parentheses (Markdown rule)":
  1730. # 2 balanced parens, 1 unbalanced:
  1731. check(dedent"""
  1732. https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst ==
  1733. dedent"""
  1734. rnInner
  1735. rnStandaloneHyperlink
  1736. rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))'
  1737. rnLeaf ')'
  1738. """)
  1739. # the same for Markdown-style link:
  1740. check(dedent"""
  1741. [foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst ==
  1742. dedent"""
  1743. rnInner
  1744. rnHyperlink
  1745. rnLeaf 'foo [bar]'
  1746. rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))'
  1747. rnLeaf ')'
  1748. """)
  1749. # unbalanced (here behavior is more RST-like actually):
  1750. check(dedent"""
  1751. https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst ==
  1752. dedent"""
  1753. rnInner
  1754. rnStandaloneHyperlink
  1755. rnLeaf 'https://en.wikipedia.org/wiki/APL_(programming_language'
  1756. rnLeaf '('
  1757. """)
  1758. # unbalanced [, but still acceptable:
  1759. check(dedent"""
  1760. [my {link example](http://example.com/bracket_(symbol_[))""".toAst ==
  1761. dedent"""
  1762. rnHyperlink
  1763. rnLeaf 'my {link example'
  1764. rnLeaf 'http://example.com/bracket_(symbol_[)'
  1765. """)
  1766. test "not a Markdown link":
  1767. # bug #17340 (27) `f` will be considered as a protocol and blocked as unsafe
  1768. var warnings = new seq[string]
  1769. check("[T](f: var Foo)".toAst(warnings = warnings) ==
  1770. dedent"""
  1771. rnInner
  1772. rnLeaf '['
  1773. rnLeaf 'T'
  1774. rnLeaf ']'
  1775. rnLeaf '('
  1776. rnLeaf 'f'
  1777. rnLeaf ':'
  1778. rnLeaf ' '
  1779. rnLeaf 'var'
  1780. rnLeaf ' '
  1781. rnLeaf 'Foo'
  1782. rnLeaf ')'
  1783. """)
  1784. check(warnings[] == @["input(1, 5) Warning: broken link 'f'"])
  1785. suite "Misc isssues":
  1786. test "Markdown CodeblockFields in one line (lacking enclosing ```)":
  1787. let message = """
  1788. ```llvm-profdata merge first.profraw second.profraw third.profraw <more stuff maybe> -output data.profdata```"""
  1789. try:
  1790. echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil)
  1791. except EParseError:
  1792. discard