$2", "$2\\label{$1}",
[id, term])
type
TIndexEntry {.pure, final.} = object
keyword: string
link: string
proc cmp(a, b: TIndexEntry): int =
result = cmpIgnoreStyle(a.keyword, b.keyword)
proc `<-`(a: var TIndexEntry, b: TIndexEntry) =
shallowCopy a.keyword, b.keyword
shallowCopy a.link, b.link
proc sortIndex(a: var openArray[TIndexEntry]) =
# we use shellsort here; fast and simple
let N = len(a)
var h = 1
while true:
h = 3 * h + 1
if h > N: break
while true:
h = h div 3
for i in countup(h, N - 1):
var v: TIndexEntry
v <- a[i]
var j = i
while cmp(a[j-h], v) >= 0:
a[j] <- a[j-h]
j = j-h
if j < h: break
a[j] <- v
if h == 1: break
proc mergeIndexes*(dir: string): string =
## merges all index files in `dir` and returns the generated index as HTML.
## The result is no full HTML for flexibility.
var a: seq[TIndexEntry]
newSeq(a, 15_000)
setLen(a, 0)
var L = 0
for kind, path in walkDir(dir):
if kind == pcFile and path.endsWith(IndexExt):
for line in lines(path):
let s = line.find('\t')
if s < 0: continue
setLen(a, L+1)
a[L].keyword = line.substr(0, s-1)
a[L].link = line.substr(s+1)
inc L
sortIndex(a)
result = ""
var i = 0
while i < L:
result.addf("$1- \n",
a[i].keyword)
var j = i
while j < L and a[i].keyword == a[j].keyword:
result.addf(
"
- $1
\n",
a[j].link)
inc j
result.add("
\n")
i = j
# ----------------------------------------------------------------------------
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)
var refname = rstnodeToRefname(n)
if d.hasToc:
var length = len(d.tocPart)
setlen(d.tocPart, length + 1)
d.tocPart[length].refname = refname
d.tocPart[length].n = n
d.tocPart[length].header = tmp
dispA(d.target, result,
"$3",
"\\rsth$4{$3}\\label{$2}\n", [$n.level,
d.tocPart[length].refname, tmp,
$chr(n.level - 1 + ord('A'))])
else:
dispA(d.target, result, "$3",
"\\rsth$4{$3}\\label{$2}\n", [
$n.level, refname, tmp,
$chr(n.level - 1 + ord('A'))])
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
if d.meta[metaTitle].len == 0:
for i in countup(0, len(n)-1):
renderRstToOut(d, n.sons[i], d.meta[metaTitle])
elif d.meta[metaSubtitle].len == 0:
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)
dispA(d.target, result, "$3",
"\\rstov$4{$3}\\label{$2}\n", [$n.level,
rstnodeToRefname(n), tmp, $chr(n.level - 1 + ord('A'))])
proc renderTocEntry(d: PDoc, e: TTocEntry, result: var string) =
dispA(d.target, result,
"$2\n",
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
proc renderTocEntries*(d: PDoc, j: var int, lvl: int, result: var string) =
var tmp = ""
while j <= high(d.tocPart):
var a = abs(d.tocPart[j].n.level)
if a == lvl:
renderTocEntry(d, d.tocPart[j], tmp)
inc(j)
elif a > lvl:
renderTocEntries(d, j, a, tmp)
else:
break
if lvl > 1:
dispA(d.target, result, "",
"\\begin{enumerate}$1\\end{enumerate}", [tmp])
else:
result.add(tmp)
proc renderImage(d: PDoc, n: PRstNode, result: var string) =
var options = ""
var s = getFieldValue(n, "scale")
if s != "": dispA(d.target, options, " scale=\"$1\"", " scale=$1", [strip(s)])
s = getFieldValue(n, "height")
if s != "": dispA(d.target, options, " height=\"$1\"", " height=$1", [strip(s)])
s = getFieldValue(n, "width")
if s != "": dispA(d.target, options, " width=\"$1\"", " width=$1", [strip(s)])
s = getFieldValue(n, "alt")
if s != "": dispA(d.target, options, " alt=\"$1\"", "", [strip(s)])
s = getFieldValue(n, "align")
if s != "": dispA(d.target, options, " align=\"$1\"", "", [strip(s)])
if options.len > 0: options = dispF(d.target, "$1", "[$1]", [options])
dispA(d.target, result, "", "\\includegraphics$2{$1}",
[getArgument(n), options])
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
dispA(d.target, result,
"""""",
"\\includegraphics{$1}", [n.text])
proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
if n.sons[2] == nil: return
var m = n.sons[2].sons[0]
assert m.kind == rnLeaf
var langstr = strip(getArgument(n))
var lang: TSourceLanguage
if langstr == "":
lang = langNimrod # default language
else:
lang = getSourceLanguage(langstr)
dispA(d.target, result, "", "\\begin{rstpre}\n")
if lang == langNone:
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr)
result.add(m.text)
else:
var g: TGeneralTokenizer
initGeneralTokenizer(g, m.text)
while true:
getNextToken(g, lang)
case g.kind
of gtEof: break
of gtNone, gtWhitespace:
add(result, substr(m.text, g.start, g.length + g.start - 1))
else:
dispA(d.target, result, "$1", "\\span$2{$1}", [
esc(d.target, substr(m.text, g.start, g.length+g.start-1)),
tokenClassToStr[g.kind]])
deinitGeneralTokenizer(g)
dispA(d.target, result, "
", "\n\\end{rstpre}\n")
proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
var tmp = ""
renderRstToOut(d, n.sons[2], tmp)
var arg = strip(getArgument(n))
if arg == "":
dispA(d.target, result, "$1
", "$1", [tmp])
else:
dispA(d.target, result, "$2
", "$2", [arg, tmp])
proc texColumns(n: PRstNode): string =
result = ""
for i in countup(1, len(n)): add(result, "|X")
proc renderField(d: PDoc, n: PRstNode, result: var string) =
var b = false
if d.target == outLatex:
var fieldname = addNodes(n.sons[0])
var fieldval = esc(d.target, strip(addNodes(n.sons[1])))
if cmpIgnoreStyle(fieldname, "author") == 0:
if d.meta[metaAuthor].len == 0:
d.meta[metaAuthor] = fieldval
b = true
elif cmpIgnoreStyle(fieldName, "version") == 0:
if d.meta[metaVersion].len == 0:
d.meta[metaVersion] = fieldval
b = true
if not b:
renderAux(d, n, "$1
\n", "$1", result)
proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
if n == nil: return
case n.kind
of rnInner: renderAux(d, n, result)
of rnHeadline: renderHeadline(d, n, result)
of rnOverline: renderOverline(d, n, result)
of rnTransition: renderAux(d, n, "
\n", "\\hrule\n", result)
of rnParagraph: renderAux(d, n, "$1
\n", "$1\n\n", result)
of rnBulletList:
renderAux(d, n, "\n",
"\\begin{itemize}$1\\end{itemize}\n", result)
of rnBulletItem, rnEnumItem:
renderAux(d, n, "$1\n", "\\item $1\n", result)
of rnEnumList:
renderAux(d, n, "$1
\n",
"\\begin{enumerate}$1\\end{enumerate}\n", result)
of rnDefList:
renderAux(d, n, "$1
\n",
"\\begin{description}$1\\end{description}\n", result)
of rnDefItem: renderAux(d, n, result)
of rnDefName: renderAux(d, n, "$1\n", "\\item[$1] ", result)
of rnDefBody: renderAux(d, n, "$1\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,
"",
"\\begin{description}$1\\end{description}\n",
[tmp])
of rnField: renderField(d, n, result)
of rnFieldName:
renderAux(d, n, "$1: | ", "\\item[$1:]", result)
of rnFieldBody:
renderAux(d, n, "$1 | ", " $1\n", result)
of rnIndex:
renderRstToOut(d, n.sons[2], result)
of rnOptionList:
renderAux(d, n, "",
"\\begin{description}\n$1\\end{description}\n", result)
of rnOptionListItem:
renderAux(d, n, "$1
\n", "$1", result)
of rnOptionGroup:
renderAux(d, n, "$1 | ", "\\item[$1]", result)
of rnDescription:
renderAux(d, n, "$1 | \n", " $1\n", result)
of rnOption, rnOptionString, rnOptionArgument:
doAssert false, "renderRstToOut"
of rnLiteralBlock:
renderAux(d, n, "$1
\n",
"\\begin{rstpre}\n$1\n\\end{rstpre}\n", result)
of rnQuotedLiteralBlock:
doAssert false, "renderRstToOut"
of rnLineBlock:
renderAux(d, n, "$1
", "$1\n\n", result)
of rnLineBlockItem:
renderAux(d, n, "$1
", "$1\\\\\n", result)
of rnBlockQuote:
renderAux(d, n, "$1
\n",
"\\begin{quote}$1\\end{quote}\n", result)
of rnTable, rnGridTable:
renderAux(d, n,
"",
"\\begin{table}\\begin{rsttab}{" &
texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
of rnTableRow:
if len(n) >= 1:
if d.target == outLatex:
var tmp = ""
renderRstToOut(d, n.sons[0], tmp)
for i in countup(1, len(n) - 1):
result.add(" & ")
renderRstToOut(d, n.sons[i], result)
result.add("\\\\\n\\hline\n")
else:
result.add("")
renderAux(d, n, result)
result.add("
\n")
of rnTableDataCell:
renderAux(d, n, "$1 | ", "$1", result)
of rnTableHeaderCell:
renderAux(d, n, "$1 | ", "\\textbf{$1}", result)
of rnLabel:
doAssert false, "renderRstToOut" # used for footnotes and other
of rnFootnote:
doAssert false, "renderRstToOut" # a footnote
of rnCitation:
doAssert false, "renderRstToOut" # similar to footnote
of rnRef:
var tmp = ""
renderAux(d, n, tmp)
dispA(d.target, result, "$1",
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
of rnStandaloneHyperlink:
renderAux(d, n,
"$1",
"\\href{$1}{$1}", result)
of rnHyperlink:
var tmp0 = ""
var tmp1 = ""
renderRstToOut(d, n.sons[0], tmp0)
renderRstToOut(d, n.sons[1], tmp1)
dispA(d.target, result, "$1",
"\\href{$2}{$1}",
[tmp0, tmp1])
of rnDirArg, rnRaw: renderAux(d, n, result)
of rnRawHtml:
if d.target != outLatex:
result.add addNodes(lastSon(n))
of rnRawLatex:
if d.target == outLatex:
result.add addNodes(lastSon(n))
of rnImage, rnFigure: renderImage(d, n, result)
of rnCodeBlock: renderCodeBlock(d, n, result)
of rnContainer: renderContainer(d, n, result)
of rnSubstitutionReferences, rnSubstitutionDef:
renderAux(d, n, "|$1|", "|$1|", result)
of rnDirective:
renderAux(d, n, "", "", result)
of rnGeneralRole:
var tmp0 = ""
var tmp1 = ""
renderRstToOut(d, n.sons[0], tmp0)
renderRstToOut(d, n.sons[1], tmp1)
dispA(d.target, result, "$1", "\\span$2{$1}",
[tmp0, tmp1])
of rnSub: renderAux(d, n, "$1", "\\rstsub{$1}", result)
of rnSup: renderAux(d, n, "$1", "\\rstsup{$1}", result)
of rnEmphasis: renderAux(d, n, "$1", "\\emph{$1}", result)
of rnStrongEmphasis:
renderAux(d, n, "$1", "\\textbf{$1}", result)
of rnTripleEmphasis:
renderAux(d, n, "$1",
"\\textbf{emph{$1}}", result)
of rnInterpretedText:
renderAux(d, n, "$1", "\\emph{$1}", result)
of rnIdx:
renderIndexTerm(d, n, result)
of rnInlineLiteral:
renderAux(d, n,
"$1",
"\\texttt{$1}", result)
of rnSmiley: renderSmiley(d, n, result)
of rnLeaf: result.add(esc(d.target, n.text))
of rnContents: d.hasToc = true
of rnTitle:
d.meta[metaTitle] = ""
renderRstToOut(d, n.sons[0], d.meta[metaTitle])
# -----------------------------------------------------------------------------
proc getVarIdx(varnames: openarray[string], id: string): int =
for i in countup(0, high(varnames)):
if cmpIgnoreStyle(varnames[i], id) == 0:
return i
result = -1
proc formatNamedVars*(frmt: string, varnames: openarray[string],
varvalues: openarray[string]): string =
var i = 0
var L = len(frmt)
result = ""
var num = 0
while i < L:
if frmt[i] == '$':
inc(i) # skip '$'
case frmt[i]
of '#':
add(result, varvalues[num])
inc(num)
inc(i)
of '$':
add(result, "$")
inc(i)
of '0'..'9':
var j = 0
while true:
j = (j * 10) + Ord(frmt[i]) - ord('0')
inc(i)
if i > L-1 or frmt[i] notin {'0'..'9'}: break
if j > high(varvalues) + 1:
raise newException(EInvalidValue, "invalid index: " & $j)
num = j
add(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
var id = ""
while true:
add(id, frmt[i])
inc(i)
if frmt[i] notin {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}: break
var idx = getVarIdx(varnames, id)
if idx >= 0:
add(result, varvalues[idx])
else:
raise newException(EInvalidValue, "unknown substitution var: " & id)
of '{':
var id = ""
inc(i)
while frmt[i] != '}':
if frmt[i] == '\0':
raise newException(EInvalidValue, "'}' expected")
add(id, frmt[i])
inc(i)
inc(i) # skip }
# search for the variable:
var idx = getVarIdx(varnames, id)
if idx >= 0: add(result, varvalues[idx])
else:
raise newException(EInvalidValue, "unknown substitution var: " & id)
else:
raise newException(EInvalidValue, "unknown substitution: $" & $frmt[i])
var start = i
while i < L:
if frmt[i] != '$': inc(i)
else: break
if i-1 >= start: add(result, substr(frmt, start, i - 1))
proc defaultConfig*(): PStringTable =
## creates a default configuration for HTML generation.
result = newStringTable(modeStyleInsensitive)
template setConfigVar(key, val: expr) =
result[key] = val
setConfigVar("split.item.toc", "20")
setConfigVar("doc.section", """
""")
setConfigVar("doc.section.toc", """
$sectionTitle
""")
setConfigVar("doc.item", """
$header
$desc
""")
setConfigVar("doc.item.toc", """
$name
""")
setConfigVar("doc.toc", """
""")
setConfigVar("doc.body_toc", """
$tableofcontents
$moduledesc
$content
""")
setConfigVar("doc.body_no_toc", "$moduledesc $content")
setConfigVar("doc.file", "$content")
# ---------- forum ---------------------------------------------------------
proc rstToHtml*(s: string, options: TRstParseOptions,
config: PStringTable): string =
## exported for *nimforum*.
proc myFindFile(filename: string): string =
# we don't find any files in online mode:
result = ""
const filen = "input"
var d: TRstGenerator
initRstGenerator(d, outHtml, config, filen, options, myFindFile, nil)
var dummyHasToc = false
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options)
result = ""
renderRstToOut(d, rst, result)