merged better html links #850

This commit is contained in:
Araq 2014-04-08 01:19:15 +02:00
commit 3e25d5f247
13 changed files with 559 additions and 56 deletions

View file

@ -9,11 +9,14 @@
## This module implements a generator of HTML/Latex from
## `reStructuredText`:idx: (see http://docutils.sourceforge.net/rst.html for
## information on this markup syntax). You can generate HTML output through the
## convenience proc ``rstToHtml``, which provided an input string with rst
## markup returns a string with the generated HTML. The final output is meant
## to be embedded inside a full document you provide yourself, so it won't
## contain the usual ``<header>`` or ``<body>`` parts.
## information on this markup syntax) and is used by the compiler's `docgen
## tools <docgen.html>`_.
##
## You can generate HTML output through the convenience proc ``rstToHtml``,
## which provided an input string with rst markup returns a string with the
## generated HTML. The final output is meant to be embedded inside a full
## document you provide yourself, so it won't contain the usual ``<header>`` or
## ``<body>`` parts.
##
## You can also create a ``TRstGenerator`` structure and populate it with the
## other lower level methods to finally build complete documents. This requires
@ -50,6 +53,9 @@ type
msgHandler*: TMsgHandler
filename*: string
meta*: array[TMetaEnum, string]
currentSection: string ## \
## Stores the empty string or the last headline/overline found in the rst
## document, so it can be used as a prettier name for term index generation.
PDoc = var TRstGenerator ## Alias to type less.
@ -104,6 +110,7 @@ proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
g.theIndex = ""
g.options = options
g.findFile = findFile
g.currentSection = ""
g.msgHandler = msgHandler
let s = config["split.item.toc"]
@ -227,20 +234,44 @@ proc renderAux(d: PDoc, n: PRstNode, frmtA, frmtB: string, result: var string) =
# ---------------- index handling --------------------------------------------
proc setIndexTerm*(d: var TRstGenerator, id, term: string) =
proc quoteIndexColumn(text: string): string =
## Returns a safe version of `text` for serialization to the ``.idx`` file.
##
## The returned version can be put without worries in a line based tab
## separated column text file. The following character sequence replacements
## will be performed for that goal:
##
## * ``"\\"`` => ``"\\\\"``
## * ``"\n"`` => ``"\\n"``
## * ``"\t"`` => ``"\\t"``
result = text.replace("\\", "\\\\").replace("\n", "\\n").replace("\t", "\\t")
proc unquoteIndexColumn(text: string): string =
## Returns the unquoted version generated by ``quoteIndexColumn``.
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
proc setIndexTerm*(d: var TRstGenerator, id, term: string,
linkTitle, linkDesc = "") =
## Adds a `term` to the index using the specified hyperlink identifier.
##
## The ``d.theIndex`` string will be used to append the term in the format
## ``term<tab>file#id``. The anchor will be the based on the name of the file
## currently being parsed plus the `id`, which will be appended after a hash.
## If `linkTitle` or `linkDesc` are not the empty string, two additional
## columns with their contents will be added.
##
## The index won't be written to disk unless you call ``writeIndexFile``.
## The index won't be written to disk unless you call ``writeIndexFile``. The
## purpose of the index is documented in the `docgen tools guide
## <docgen.html#index-switch>`_.
d.theIndex.add(term)
d.theIndex.add('\t')
let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt)
d.theIndex.add(htmlFile)
d.theIndex.add('#')
d.theIndex.add(id)
if linkTitle.len > 0 or linkDesc.len > 0:
d.theIndex.add('\t' & linkTitle.quoteIndexColumn)
d.theIndex.add('\t' & linkDesc.quoteIndexColumn)
d.theIndex.add("\n")
proc hash(n: PRstNode): int =
@ -256,7 +287,7 @@ proc renderIndexTerm(d: PDoc, n: PRstNode, result: var string) =
let id = rstnodeToRefname(n) & '_' & $abs(hash(n))
var term = ""
renderAux(d, n, term)
setIndexTerm(d, id, term)
setIndexTerm(d, id, term, d.currentSection)
dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
[id, term])
@ -264,13 +295,22 @@ type
TIndexEntry {.pure, final.} = object
keyword: string
link: string
linkTitle: string ## If not nil, contains a prettier text for the href
linkDesc: string ## If not nil, the title attribute of the final href
proc cmp(a, b: TIndexEntry): int =
## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`.
result = cmpIgnoreStyle(a.keyword, b.keyword)
if result == 0:
result = cmpIgnoreStyle(a.link, b.link)
proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link
if b.linkTitle.isNil: a.linkTitle = nil
else: shallowCopy a.linkTitle, b.linkTitle
if b.linkDesc.isNil: a.linkDesc = nil
else: shallowCopy a.linkDesc, b.linkDesc
proc sortIndex(a: var openArray[TIndexEntry]) =
# we use shellsort here; fast and simple
@ -307,6 +347,15 @@ proc mergeIndexes*(dir: string): string =
setLen(a, L+1)
a[L].keyword = line.substr(0, s-1)
a[L].link = line.substr(s+1)
if a[L].link.find('\t') > 0:
let extraCols = a[L].link.split('\t')
a[L].link = extraCols[0]
assert extraCols.len == 3
a[L].linkTitle = extraCols[1].unquoteIndexColumn
a[L].linkDesc = extraCols[2].unquoteIndexColumn
else:
a[L].linkTitle = nil
a[L].linkDesc = nil
inc L
sortIndex(a)
result = ""
@ -316,9 +365,17 @@ proc mergeIndexes*(dir: string): string =
[a[i].keyword])
var j = i
while j < L and a[i].keyword == a[j].keyword:
result.addf(
"<li><a class=\"reference external\" href=\"$1\">$1</a></li>\n",
[a[j].link])
let
url = a[j].link
text = if not a[j].linkTitle.isNil: a[j].linkTitle else: url
desc = if not a[j].linkDesc.isNil: a[j].linkDesc else: ""
if desc.len > 0:
result.addf("""<li><a class="reference external"
title="$3" href="$1">$2</a></li>
""", [url, text, desc])
else:
result.addf("""<li><a class="reference external" href="$1">$2</a></li>
""", [url, text])
inc j
result.add("</ul></dd>\n")
i = j
@ -328,6 +385,7 @@ proc mergeIndexes*(dir: string): string =
proc renderHeadline(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
var refname = rstnodeToRefname(n)
if d.hasToc:
var length = len(d.tocPart)
@ -349,14 +407,17 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
if d.meta[metaTitle].len == 0:
d.currentSection = d.meta[metaTitle]
for i in countup(0, len(n)-1):
renderRstToOut(d, n.sons[i], d.meta[metaTitle])
elif d.meta[metaSubtitle].len == 0:
d.currentSection = d.meta[metaSubtitle]
for i in countup(0, len(n)-1):
renderRstToOut(d, n.sons[i], d.meta[metaSubtitle])
else:
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'))])
@ -716,6 +777,8 @@ proc defaultConfig*(): PStringTable =
template setConfigVar(key, val: expr) =
result[key] = val
# If you need to modify these values, it might be worth updating the template
# file in config/nimdoc.cfg.
setConfigVar("split.item.toc", "20")
setConfigVar("doc.section", """
<div class="section" id="$sectionID">
@ -733,13 +796,14 @@ $content
</li>
""")
setConfigVar("doc.item", """
<dt id="$itemID"><pre>$header</pre></dt>
<dt id="$itemID"><a name="$itemSymOrIDEnc"></a><pre>$header</pre></dt>
<dd>
$desc
</dd>
""")
setConfigVar("doc.item.toc", """
<li><a class="reference" href="#$itemID">$name</a></li>
<li><a class="reference" href="#$itemSymOrIDEnc"
title="$header_plain">$name</a></li>
""")
setConfigVar("doc.toc", """
<div class="navigation" id="navigation">