diff options
author | Andrey Makarov <ph.makarov@gmail.com> | 2021-01-11 21:51:04 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-11 19:51:04 +0100 |
commit | fd5c8ef20845a511ad4e0af8dd8ad4331bf46ffc (patch) | |
tree | 043e7e4f09bb9fe6c82a936dca50d49d7b3737a7 /lib | |
parent | 335f849c36c4d7e618de277f6da64d8c1fdcc1c9 (diff) | |
download | Nim-fd5c8ef20845a511ad4e0af8dd8ad4331bf46ffc.tar.gz |
RST: implement internal targets (#16614)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/packages/docutils/rst.nim | 168 | ||||
-rw-r--r-- | lib/packages/docutils/rstast.nim | 14 | ||||
-rw-r--r-- | lib/packages/docutils/rstgen.nim | 135 |
3 files changed, 218 insertions, 99 deletions
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 8d16edc61..698b973e2 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,10 +7,19 @@ # distribution, for details about the copyright. # -## This module implements a `reStructuredText`:idx: (RST) parser. A large -## subset is implemented. Some features of the `markdown`:idx: syntax are -## also supported. Nim can output the result to HTML (command ``rst2html``) -## or Latex (command ``rst2tex``). +## ================================== +## rst: Nim-flavored reStructuredText +## ================================== +## +## This module implements a `reStructuredText`:idx: (RST) parser. +## A large subset is implemented with some limitations_ and +## `Nim-specific features`_. +## A few `extra features`_ of the `Markdown`:idx: syntax are +## also supported. +## +## Nim can output the result to HTML (commands ``nim doc`` for +## ``*.nim`` files and ``nim rst2html`` for ``*.rst`` files) or +## Latex (command ``nim rst2tex`` for ``*.rst``). ## ## If you are new to RST please consider reading the following: ## @@ -18,6 +27,9 @@ ## 2) an `RST reference`_: a comprehensive cheatsheet for RST ## 3) a more formal 50-page `RST specification`_. ## +## Features +## -------- +## ## Supported standard RST features: ## ## * body elements @@ -43,27 +55,33 @@ ## + comments ## * inline markup ## + *emphasis*, **strong emphasis**, -## ``inline literals``, hyperlink references, substitution references, -## standalone hyperlinks +## ``inline literals``, hyperlink references (including embedded URI), +## substitution references, standalone hyperlinks, +## internal links (inline and outline) ## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, ## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:supscript:`` ## (see `RST roles list`_ for description). +## + inline internal targets +## +## .. _`Nim-specific features`: ## -## Additional features: +## Additional Nim-specific features: ## ## * directives: ``code-block``, ``title``, ``index`` ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). ## +## .. _`extra features`: +## ## Optional additional features, turned on by ``options: RstParseOption`` in ## `rstParse proc <#rstParse,string,string,int,int,bool,RstParseOptions,FindFileHandler,MsgHandler>`_: ## ## * emoji / smiley symbols -## * markdown tables -## * markdown code blocks -## * markdown links -## * markdown headlines +## * Markdown tables +## * Markdown code blocks +## * Markdown links +## * Markdown headlines ## * using ``1`` as auto-enumerator in enumerated lists like RST ``#`` ## (auto-enumerator ``1`` can not be used with ``#`` in the same list) ## @@ -73,7 +91,8 @@ ## .. warning:: Using Nim-specific features can cause other RST implementations ## to fail on your document. ## -## Limitations: +## Limitations +## ----------- ## ## * no Unicode support in character width calculations ## * body elements @@ -89,10 +108,21 @@ ## - no ``role`` directives and no custom interpreted text roles ## - some standard roles are not supported (check `RST roles list`_) ## - no footnotes & citations support -## - no inline internal targets ## * inline markup ## - no simple-inline-markup -## - no embedded URI and aliases +## - no embedded aliases +## +## Usage +## ----- +## +## See `Nim DocGen Tools Guide <docgen.html>`_ for the details about +## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. +## +## See `packages/docutils/rstgen module <rstgen.html>`_ to know how to +## generate HTML or Latex strings to embed them into your documents. +## +## .. Tip:: Import ``packages/docutils/rst`` to use this module +## programmatically. ## ## .. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html ## .. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html @@ -100,13 +130,6 @@ ## .. _RST directives list: https://docutils.sourceforge.io/docs/ref/rst/directives.html ## .. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html ## .. _Nim index: https://nim-lang.org/docs/theindex.html -## -## See `Nim DocGen Tools Guide <docgen.html>`_ for the details about -## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. -## -## .. note:: Import ``packages/docutils/rst`` to use this module. -## -## See also `packages/docutils/rstgen module <rstgen.html>`_. import os, strutils, rstast @@ -118,7 +141,7 @@ type roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) - roSupportMarkdown ## support additional features of markdown + roSupportMarkdown ## support additional features of Markdown RstParseOptions* = set[RstParseOption] @@ -131,7 +154,7 @@ type meCannotOpenFile = "cannot open '$1'", meExpected = "'$1' expected", meGridTableNotImplemented = "grid table is not implemented", - meMarkdownIllformedTable = "illformed delimiter row of a markdown table", + meMarkdownIllformedTable = "illformed delimiter row of a Markdown table", meNewSectionExpected = "new section expected", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", @@ -379,12 +402,16 @@ type Substitution = object key*: string value*: PRstNode + AnchorSubst = tuple + mainAnchor: string + aliases: seq[string] SharedState = object options: RstParseOptions # parsing options uLevel, oLevel: int # counters for the section levels subs: seq[Substitution] # substitutions refs: seq[Substitution] # references + anchors: seq[AnchorSubst] # internal target substitutions underlineToLevel: LevelMap # Saves for each possible title adornment # character its level in the # current document. @@ -405,6 +432,7 @@ type filename*: string line*, col*: int hasToc*: bool + curAnchor*: string # variable to track latest anchor in s.anchors EParseError* = object of ValueError @@ -577,6 +605,38 @@ proc findRef(p: var RstParser, key: string): PRstNode = if key == p.s.refs[i].key: return p.s.refs[i].value +proc addAnchor(p: var RstParser, refn: string, reset: bool) = + ## add anchor `refn` to anchor aliases and update last anchor ``curAnchor`` + if p.curAnchor == "": + p.s.anchors.add (refn, @[refn]) + else: + p.s.anchors[^1].mainAnchor = refn + p.s.anchors[^1].aliases.add refn + if reset: + p.curAnchor = "" + else: + p.curAnchor = refn + +proc findMainAnchor(p: RstParser, refn: string): string = + for subst in p.s.anchors: + if subst.mainAnchor == refn: # no need to rename + result = subst.mainAnchor + break + var toLeave = false + for anchor in subst.aliases: + if anchor == refn: # this anchor will be named as mainAnchor + result = subst.mainAnchor + toLeave = true + if toLeave: + break + +proc newRstNodeA(p: var RstParser, kind: RstNodeKind): PRstNode = + ## create node and consume the current anchor + result = newRstNode(kind) + if p.curAnchor != "": + result.anchor = p.curAnchor + p.curAnchor = "" + proc newLeaf(p: var RstParser): PRstNode = result = newRstNode(rnLeaf, currentTok(p).symbol) @@ -629,7 +689,10 @@ proc isInlineMarkupEnd(p: RstParser, markup: string): bool = proc isInlineMarkupStart(p: RstParser, markup: string): bool = # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules var d: char - result = currentTok(p).symbol == markup + if markup != "_`": + result = currentTok(p).symbol == markup + else: # _` is a 2 token case + result = currentTok(p).symbol == "_" and nextTok(p).symbol == "`" if not result: return # Rule 6: result = p.idx == 0 or prevTok(p).kind in {tkIndent, tkWhite} or @@ -873,7 +936,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = inc p.idx var lb = newRstNode(rnLiteralBlock) lb.add(n) - result = newRstNode(rnCodeBlock) + result = newRstNodeA(p, rnCodeBlock) result.add(args) result.add(PRstNode(nil)) result.add(lb) @@ -918,6 +981,13 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) father.add(n) + elif isInlineMarkupStart(p, "_`"): + var n = newRstNode(rnInlineTarget) + inc p.idx + parseUntil(p, n, "`", false) + let refn = rstnodeToRefname(n) + p.s.anchors.add (refn, @[refn]) + father.add(n) elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": inc p.idx father.add(parseMarkdownCodeblock(p)) @@ -1049,7 +1119,7 @@ proc parseFields(p: var RstParser): PRstNode = if currentTok(p).kind == tkIndent and nextTok(p).symbol == ":" or atStart: var col = if atStart: currentTok(p).col else: currentTok(p).ival - result = newRstNode(rnFieldList) + result = newRstNodeA(p, rnFieldList) if not atStart: inc p.idx while true: result.add(parseField(p)) @@ -1090,7 +1160,7 @@ proc getArgument(n: PRstNode): string = proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.} proc parseLiteralBlock(p: var RstParser): PRstNode = - result = newRstNode(rnLiteralBlock) + result = newRstNodeA(p, rnLiteralBlock) var n = newRstNode(rnLeaf, "") if currentTok(p).kind == tkIndent: var indent = currentTok(p).ival @@ -1248,7 +1318,7 @@ proc parseLineBlock(p: var RstParser): PRstNode = result = nil if nextTok(p).kind in {tkWhite, tkIndent}: var col = currentTok(p).col - result = newRstNode(rnLineBlock) + result = newRstNodeA(p, rnLineBlock) while true: var item = newRstNode(rnLineBlockItem) if nextTok(p).kind == tkWhite: @@ -1314,6 +1384,7 @@ proc parseHeadline(p: var RstParser): PRstNode = var c = nextTok(p).symbol[0] inc p.idx, 2 result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) + addAnchor(p, rstnodeToRefname(result), reset=true) type IntSeq = seq[int] @@ -1349,7 +1420,7 @@ proc parseSimpleTable(p: var RstParser): PRstNode = c: char q: RstParser a, b: PRstNode - result = newRstNode(rnTable) + result = newRstNodeA(p, rnTable) cols = @[] row = @[] a = nil @@ -1428,7 +1499,7 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = colNum: int a, b: PRstNode q: RstParser - result = newRstNode(rnMarkdownTable) + result = newRstNodeA(p, rnMarkdownTable) proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) = row = readTableRow(p) @@ -1452,7 +1523,7 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = parseRow(p, rnTableDataCell, result) proc parseTransition(p: var RstParser): PRstNode = - result = newRstNode(rnTransition) + result = newRstNodeA(p, rnTransition) inc p.idx if currentTok(p).kind == tkIndent: inc p.idx if currentTok(p).kind == tkIndent: inc p.idx @@ -1475,13 +1546,14 @@ proc parseOverline(p: var RstParser): PRstNode = if currentTok(p).kind == tkAdornment: inc p.idx # XXX: check? if currentTok(p).kind == tkIndent: inc p.idx + addAnchor(p, rstnodeToRefname(result), reset=true) proc parseBulletList(p: var RstParser): PRstNode = result = nil if nextTok(p).kind == tkWhite: var bullet = currentTok(p).symbol var col = currentTok(p).col - result = newRstNode(rnBulletList) + result = newRstNodeA(p, rnBulletList) pushInd(p, p.tok[p.idx + 2].col) inc p.idx, 2 while true: @@ -1497,7 +1569,7 @@ proc parseBulletList(p: var RstParser): PRstNode = popInd(p) proc parseOptionList(p: var RstParser): PRstNode = - result = newRstNode(rnOptionList) + result = newRstNodeA(p, rnOptionList) while true: if isOptionList(p): var a = newRstNode(rnOptionGroup) @@ -1530,7 +1602,7 @@ proc parseDefinitionList(p: var RstParser): PRstNode = if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > currInd(p) and p.tok[j - 1].symbol != "::": var col = currentTok(p).col - result = newRstNode(rnDefList) + result = newRstNodeA(p, rnDefList) while true: j = p.idx var a = newRstNode(rnDefName) @@ -1568,7 +1640,7 @@ proc parseEnumList(p: var RstParser): PRstNode = wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3] # number of tokens wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0] # position of enumeration sequence (number/letter) in enumerator - result = newRstNode(rnEnumList) + result = newRstNodeA(p, rnEnumList) let col = currentTok(p).col var w = 0 while w < wildcards.len: @@ -1623,6 +1695,7 @@ proc sonKind(father: PRstNode, i: int): RstNodeKind = if i < father.len: result = father.sons[i].kind proc parseSection(p: var RstParser, result: PRstNode) = + ## parse top-level RST elements: sections, transitions and body elements. while true: var leave = false assert(p.idx >= 0) @@ -1631,7 +1704,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = inc p.idx elif currentTok(p).ival > currInd(p): pushInd(p, currentTok(p).ival) - var a = newRstNode(rnBlockQuote) + var a = newRstNodeA(p, rnBlockQuote) parseSection(p, a) result.add(a) popInd(p) @@ -1667,7 +1740,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = #InternalError("rst.parseSection()") discard if a == nil and k != rnDirective: - a = newRstNode(rnParagraph) + a = newRstNodeA(p, rnParagraph) parseParagraph(p, a) result.addIfNotNil(a) if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: @@ -1703,7 +1776,7 @@ proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode = ## ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to ## check them before accessing. - result = newRstNode(rnDirective) + result = newRstNodeA(p, rnDirective) var args: PRstNode = nil var options: PRstNode = nil if hasArg in flags: @@ -1981,7 +2054,10 @@ proc parseDotDot(p: var RstParser): PRstNode = var a = getReferenceName(p, ":") if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) - setRef(p, rstnodeToRefname(a), b) + if len(b) == 0 and b.text == "": # set internal anchor + addAnchor(p, rstnodeToRefname(a), reset=false) + else: # external hyperlink + setRef(p, rstnodeToRefname(a), b) elif match(p, p.idx, " |"): # substitution definitions: inc p.idx, 2 @@ -2009,6 +2085,7 @@ proc parseDotDot(p: var RstParser): PRstNode = result = parseComment(p) proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = + ## resolve substitutions and anchor aliases result = n if n == nil: return case n.kind @@ -2022,12 +2099,20 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = if e != "": result = newRstNode(rnLeaf, e) else: rstMessage(p, mwUnknownSubstitution, key) of rnRef: - var y = findRef(p, rstnodeToRefname(n)) + let refn = rstnodeToRefname(n) + var y = findRef(p, refn) if y != nil: result = newRstNode(rnHyperlink) n.kind = rnInner result.add(n) result.add(y) + else: + let s = findMainAnchor(p, refn) + if s != "": + result = newRstNode(rnInternalRef) + n.kind = rnInner + result.add(n) + result.add(newRstNode(rnLeaf, s)) of rnLeaf: discard of rnContents: @@ -2045,5 +2130,6 @@ proc rstParse*(text, filename: string, p.filename = filename p.line = line p.col = column + getTokens(text, roSkipPounds in options, p.tok) - result = resolveSubs(p, parseDoc(p)) + let unresolved = parseDoc(p) + result = resolveSubs(p, unresolved) hasToc = p.hasToc diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index e4e192fa3..4e3ca9a2b 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -42,7 +42,7 @@ type rnLabel, # used for footnotes and other things rnFootnote, # a footnote rnCitation, # similar to footnote - rnStandaloneHyperlink, rnHyperlink, rnRef, + rnStandaloneHyperlink, rnHyperlink, rnRef, rnInternalRef, rnDirective, # a general directive rnDirArg, # a directive argument (for some directives). # here are directives that are not rnDirective: @@ -62,6 +62,7 @@ type rnTripleEmphasis, # "***" rnInterpretedText, # "`" rnInlineLiteral, # "``" + rnInlineTarget, # "_`target`" rnSubstitutionReferences, # "|" rnSmiley, # some smiley rnLeaf # a leaf; the node's text field contains the @@ -75,7 +76,9 @@ type text*: string ## valid for leafs in the AST; and the title of ## the document or the section; and rnEnumList ## and rnAdmonition; and rnLineBlockItem - level*: int ## valid for some node kinds + level*: int ## valid for headlines/overlines only + anchor*: string ## anchor, internal link target + ## (aka HTML id tag, aka Latex label/hypertarget) sons*: RstNodeSeq ## the node's sons proc len*(n: PRstNode): int = @@ -330,8 +333,9 @@ proc renderRstToStr*(node: PRstNode, indent=0): string = if node == nil: result.add " ".repeat(indent) & "[nil]\n" return - result.add " ".repeat(indent) & $node.kind & "\t" & - (if node.text == "": "" else: "'" & node.text & "'") & - (if node.level == 0: "" else: "\tlevel=" & $node.level) & "\n" + result.add " ".repeat(indent) & $node.kind & + (if node.text == "": "" else: "\t'" & node.text & "'") & + (if node.level == 0: "" else: "\tlevel=" & $node.level) & + (if node.anchor == "": "" else: "\tanchor='" & node.anchor & "'") & "\n" for son in node.sons: result.add renderRstToStr(son, indent=indent+2) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 52125b52c..1dd299ee4 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -272,13 +272,25 @@ proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string) proc renderAux(d: PDoc, n: PRstNode, result: var string) = for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result) -proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) = +template idS(txt: string): string = + if txt == "": "" + else: + case d.target + of outHtml: + " id=\"" & txt & "\"" + of outLatex: + "\\label{" & txt & "}\\hypertarget{" & txt & "}{}" + # we add \label for page number references via \pageref, while + # \hypertarget is for clickable links via \hyperlink. + +proc renderAux(d: PDoc, n: PRstNode, html, tex: string, result: var string) = + # formats sons of `n` as substitution variable $1 inside strings `html` and + # `tex`, internal target (anchor) is provided as substitute $2. var tmp = "" for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], tmp) - if d.target != outLatex: - result.addf(frmtA, [tmp]) - else: - result.addf(frmtB, [tmp]) + case d.target + of outHtml: result.addf(html, [tmp, n.anchor.idS]) + of outLatex: result.addf(tex, [tmp, n.anchor.idS]) # ---------------- index handling -------------------------------------------- @@ -746,13 +758,13 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].n = n d.tocPart[length].header = tmp - dispA(d.target, result, "\n<h$1><a class=\"toc-backref\" " & - "id=\"$2\" href=\"#$2\">$3</a></h$1>", "\\rsth$4{$3}\\label{$2}\n", - [$n.level, d.tocPart[length].refname, tmp, $chr(n.level - 1 + ord('A'))]) + dispA(d.target, result, "\n<h$1><a class=\"toc-backref\"" & + "$2 href=\"#$5\">$3</a></h$1>", "\\rsth$4{$3}$2\n", + [$n.level, refname.idS, tmp, $chr(n.level - 1 + ord('A')), refname]) else: - dispA(d.target, result, "\n<h$1 id=\"$2\">$3</h$1>", - "\\rsth$4{$3}\\label{$2}\n", [ - $n.level, refname, tmp, + dispA(d.target, result, "\n<h$1$2>$3</h$1>", + "\\rsth$4{$3}$2\n", [ + $n.level, refname.idS, tmp, $chr(n.level - 1 + ord('A'))]) # Generate index entry using spaces to indicate TOC level for the output HTML. @@ -781,9 +793,9 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) d.currentSection = tmp - dispA(d.target, result, "<h$1 id=\"$2\"><center>$3</center></h$1>", - "\\rstov$4{$3}\\label{$2}\n", [$n.level, - rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))]) + dispA(d.target, result, "<h$1$2><center>$3</center></h$1>", + "\\rstov$4{$3}$2\n", [$n.level, + rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A'))]) proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = @@ -841,12 +853,12 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = if arg.endsWith(".mp4") or arg.endsWith(".ogg") or arg.endsWith(".webm"): htmlOut = """ - <video src="$1"$2 autoPlay='true' loop='true' muted='true'> + <video$3 src="$1"$2 autoPlay='true' loop='true' muted='true'> Sorry, your browser doesn't support embedded videos </video> """ else: - htmlOut = "<img src=\"$1\"$2/>" + htmlOut = "<img$3 src=\"$1\"$2/>" # support for `:target:` links for images: var target = esc(d.target, getFieldValue(n, "target").strip()) @@ -859,8 +871,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = "\\href{$2}{$1}", [htmlOut, target]) htmlOut = htmlOutWithLink - dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}", - [esc(d.target, arg), options]) + dispA(d.target, result, htmlOut, "$3\\includegraphics$2{$1}", + [esc(d.target, arg), options, n.anchor.idS]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = @@ -925,7 +937,8 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = if result.langStr != "": result.lang = getSourceLanguage(result.langStr) -proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string): +proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, + idStr: string): tuple[beginTable, endTable: string] = ## Returns the necessary tags to start/end a code block in HTML. ## @@ -937,21 +950,22 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string): let id = $d.listingCounter if not params.numberLines: result = (d.config.getOrDefault"doc.listing_start" % - [id, sourceLanguageToStr[params.lang]], + [id, sourceLanguageToStr[params.lang], idStr], d.config.getOrDefault"doc.listing_end" % id) return var codeLines = code.strip.countLines assert codeLines > 0 - result.beginTable = """<table class="line-nums-table"><tbody><tr><td class="blob-line-nums"><pre class="line-nums">""" + result.beginTable = """<table$1 class="line-nums-table">""" % [idStr] & + """<tbody><tr><td class="blob-line-nums"><pre class="line-nums">""" var line = params.startLine while codeLines > 0: result.beginTable.add($line & "\n") line.inc codeLines.dec - result.beginTable.add("</pre></td><td>" & ( + result.beginTable.add("</pre$3></td><td>" & ( d.config.getOrDefault"doc.listing_start" % - [id, sourceLanguageToStr[params.lang]])) + [id, sourceLanguageToStr[params.lang], idStr])) result.endTable = (d.config.getOrDefault"doc.listing_end" % id) & "</td></tr></tbody></table>" & ( d.config.getOrDefault"doc.listing_button" % id) @@ -975,9 +989,10 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = if params.testCmd.len > 0 and d.onTestSnippet != nil: d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text) - let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text) - - dispA(d.target, result, blockStart, "\\begin{rstpre}\n", []) + let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, + n.anchor.idS) + dispA(d.target, result, blockStart, + "\\begin{rstpre}\n" & n.anchor.idS & "\n", []) if params.lang == langNone: if len(params.langStr) > 0: d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) @@ -1075,8 +1090,8 @@ proc renderEnumList(d: PDoc, n: PRstNode, result: var string) = if n.text[i1] != first: specStart = " start=\"$1\"" % [ $(ord(n.text[i1]) - ord(first) + 1) ] specifier = labelDef & specStart - renderAux(d, n, "<ol " & specifier & ">$1</ol>\n", - "\\begin{enumerate}" & specifier & "$1\\end{enumerate}\n", + renderAux(d, n, "<ol$2 " & specifier & ">$1</ol>\n", + "\\begin{enumerate}" & specifier & "$2$1\\end{enumerate}\n", result) proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = @@ -1095,9 +1110,9 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = let txt = n.text.capitalizeAscii() let htmlHead = "<div class=\"admonition " & htmlCls & "\">" renderAux(d, n, - htmlHead & "<span class=\"" & htmlCls & "-text\"><b>" & txt & + htmlHead & "<span$2 class=\"" & htmlCls & "-text\"><b>" & txt & ":</b></span>\n" & "$1</div>\n", - "\n\n\\begin{mdframed}[linecolor=" & texColor & "]\n" & + "\n\n\\begin{mdframed}[linecolor=" & texColor & "]$2\n" & "{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " & "$1\n\\end{mdframed}\n", result) @@ -1108,33 +1123,33 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnInner: renderAux(d, n, result) of rnHeadline: renderHeadline(d, n, result) of rnOverline: renderOverline(d, n, result) - of rnTransition: renderAux(d, n, "<hr />\n", "\\hrule\n", result) - of rnParagraph: renderAux(d, n, "<p>$1</p>\n", "$1\n\n", result) + of rnTransition: renderAux(d, n, "<hr$2 />\n", "\\hrule$2\n", result) + of rnParagraph: renderAux(d, n, "<p$2>$1</p>\n", "$2\n$1\n\n", result) of rnBulletList: - renderAux(d, n, "<ul class=\"simple\">$1</ul>\n", - "\\begin{itemize}$1\\end{itemize}\n", result) + renderAux(d, n, "<ul$2 class=\"simple\">$1</ul>\n", + "\\begin{itemize}\n$2\n$1\\end{itemize}\n", result) of rnBulletItem, rnEnumItem: - renderAux(d, n, "<li>$1</li>\n", "\\item $1\n", result) + renderAux(d, n, "<li$2>$1</li>\n", "\\item $2$1\n", result) of rnEnumList: renderEnumList(d, n, result) of rnDefList: - renderAux(d, n, "<dl class=\"docutils\">$1</dl>\n", - "\\begin{description}$1\\end{description}\n", result) + renderAux(d, n, "<dl$2 class=\"docutils\">$1</dl>\n", + "\\begin{description}\n$2\n$1\\end{description}\n", result) of rnDefItem: renderAux(d, n, result) - of rnDefName: renderAux(d, n, "<dt>$1</dt>\n", "\\item[$1] ", result) - of rnDefBody: renderAux(d, n, "<dd>$1</dd>\n", "$1\n", result) + of rnDefName: renderAux(d, n, "<dt$2>$1</dt>\n", "$2\\item[$1] ", result) + of rnDefBody: renderAux(d, n, "<dd$2>$1</dd>\n", "$2\n$1\n", result) of rnFieldList: var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) if tmp.len != 0: dispA(d.target, result, - "<table class=\"docinfo\" frame=\"void\" rules=\"none\">" & + "<table$2 class=\"docinfo\" frame=\"void\" rules=\"none\">" & "<col class=\"docinfo-name\" />" & "<col class=\"docinfo-content\" />" & "<tbody valign=\"top\">$1" & "</tbody></table>", - "\\begin{description}$1\\end{description}\n", - [tmp]) + "\\begin{description}\n$2\n$1\\end{description}\n", + [tmp, n.anchor.idS]) of rnField: renderField(d, n, result) of rnFieldName: renderAux(d, n, "<th class=\"docinfo-name\">$1:</th>", @@ -1144,8 +1159,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnIndex: renderRstToOut(d, n.sons[2], result) of rnOptionList: - renderAux(d, n, "<table frame=\"void\">$1</table>", - "\\begin{description}\n$1\\end{description}\n", result) + renderAux(d, n, "<table$2 frame=\"void\">$1</table>", + "\\begin{description}\n$2\n$1\\end{description}\n", result) of rnOptionListItem: renderAux(d, n, "<tr>$1</tr>\n", "$1", result) of rnOptionGroup: @@ -1155,16 +1170,17 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnOption, rnOptionString, rnOptionArgument: doAssert false, "renderRstToOut" of rnLiteralBlock: - renderAux(d, n, "<pre>$1</pre>\n", - "\\begin{rstpre}\n$1\n\\end{rstpre}\n", result) + renderAux(d, n, "<pre$2>$1</pre>\n", + "\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n", result) of rnQuotedLiteralBlock: doAssert false, "renderRstToOut" of rnLineBlock: if n.sons.len == 1 and n.sons[0].text == "\n": # whole line block is one empty line, no need to add extra spacing - renderAux(d, n, "<p>$1</p> ", "\n\n$1", result) + renderAux(d, n, "<p$2>$1</p> ", "\n\n$2\n$1", result) else: # add extra spacing around the line block for Latex - renderAux(d, n, "<p>$1</p>", "\n\\vspace{0.5em}\n$1\\vspace{0.5em}\n", result) + renderAux(d, n, "<p$2>$1</p>", + "\n\\vspace{0.5em}$2\n$1\\vspace{0.5em}\n", result) of rnLineBlockItem: if n.text.len == 0: # normal case - no additional indentation renderAux(d, n, "$1<br/>", "\\noindent $1\n\n", result) @@ -1176,13 +1192,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "<span style=\"margin-left: " & indent & "\">$1</span><br/>", "\\noindent\\hspace{" & indent & "}$1\n\n", result) of rnBlockQuote: - renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n", - "\\begin{quote}$1\\end{quote}\n", result) + renderAux(d, n, "<blockquote$2><p>$1</p></blockquote>\n", + "\\begin{quote}\n$2\n$1\\end{quote}\n", result) of rnAdmonition: renderAdmonition(d, n, result) of rnTable, rnGridTable, rnMarkdownTable: renderAux(d, n, - "<table border=\"1\" class=\"docutils\">$1</table>", - "\\begin{table}\\begin{rsttab}{" & + "<table$2 border=\"1\" class=\"docutils\">$1</table>", + "\\begin{table}\n$2\n\\begin{rsttab}{" & texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result) of rnTableRow: if len(n) >= 1: @@ -1217,6 +1233,12 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "<a class=\"reference external\" href=\"$1\">$1</a>", "\\href{$1}{$1}", result) + of rnInternalRef: + var tmp = "" + renderAux(d, n.sons[0], tmp) + dispA(d.target, result, + "<a class=\"reference internal\" href=\"#$2\">$1</a>", + "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [tmp, n.sons[1].text]) of rnHyperlink: var tmp0 = "" var tmp1 = "" @@ -1261,6 +1283,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>", "\\texttt{$1}", result) + of rnInlineTarget: + var tmp = "" + renderAux(d, n, tmp) + dispA(d.target, result, + "<span class=\"target\" id=\"$2\">$1</span>", + "\\label{$2}\\hypertarget{$2}{$1}", + [tmp, rstnodeToRefname(n)]) of rnSmiley: renderSmiley(d, n, result) of rnLeaf: result.add(esc(d.target, n.text)) of rnContents: d.hasToc = true @@ -1396,7 +1425,7 @@ $moduledesc $content </div> """) - setConfigVar("doc.listing_start", "<pre class = \"listing\">") + setConfigVar("doc.listing_start", "<pre$3 class = \"listing\">") setConfigVar("doc.listing_end", "</pre>") setConfigVar("doc.listing_button", "</pre>") setConfigVar("doc.body_no_toc", "$moduledesc $content") |