diff options
Diffstat (limited to 'rod/docgen.nim')
-rwxr-xr-x | rod/docgen.nim | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/rod/docgen.nim b/rod/docgen.nim new file mode 100755 index 000000000..3a69f963c --- /dev/null +++ b/rod/docgen.nim @@ -0,0 +1,915 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2009 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# This is the documentation generator. It is currently pretty simple: No +# semantic checking is done for the code. Cross-references are generated +# by knowing how the anchors are going to be named. + +import + ast, astalgo, strutils, nhashes, options, nversion, msgs, os, ropes, idents, + wordrecg, math, syntaxes, rnimsyn, scanner, rst, times, highlite + +proc CommandDoc*(filename: string) +proc CommandRst2Html*(filename: string) +proc CommandRst2TeX*(filename: string) +# implementation + +type + TTocEntry{.final.} = object + n*: PRstNode + refname*, header*: PRope + + TSections = array[TSymKind, PRope] + TMetaEnum = enum + metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion + TDocumentor{.final.} = object # contains a module's documentation + filename*: string # filename of the source file; without extension + basedir*: string # base directory (where to put the documentation) + modDesc*: PRope # module description + dependsOn*: PRope # dependencies + id*: int # for generating IDs + splitAfter*: int # split too long entries in the TOC + tocPart*: seq[TTocEntry] + hasToc*: bool + toc*, section*: TSections + indexFile*, theIndex*: PRstNode + indexValFilename*: string + indent*, verbatim*: int # for code generation + meta*: array[TMetaEnum, PRope] + + PDoc = ref TDocumentor + +var splitter: string = "<wbr />" + +proc findIndexNode(n: PRstNode): PRstNode = + if n == nil: + result = nil + elif n.kind == rnIndex: + result = n.sons[2] + if result == nil: + result = newRstNode(rnDefList) + n.sons[2] = result + elif result.kind == rnInner: + result = result.sons[0] + else: + result = nil + for i in countup(0, rsonsLen(n) - 1): + result = findIndexNode(n.sons[i]) + if result != nil: return + +proc initIndexFile(d: PDoc) = + var + h: PRstNode + dummyHasToc: bool + if gIndexFile == "": return + gIndexFile = addFileExt(gIndexFile, "txt") + d.indexValFilename = changeFileExt(extractFilename(d.filename), HtmlExt) + if ExistsFile(gIndexFile): + d.indexFile = rstParse(readFile(gIndexFile), false, gIndexFile, 0, 1, + dummyHasToc) + d.theIndex = findIndexNode(d.indexFile) + if (d.theIndex == nil) or (d.theIndex.kind != rnDefList): + rawMessage(errXisNoValidIndexFile, gIndexFile) + clearIndex(d.theIndex, d.indexValFilename) + else: + d.indexFile = newRstNode(rnInner) + h = newRstNode(rnOverline) + h.level = 1 + addSon(h, newRstNode(rnLeaf, "Index")) + addSon(d.indexFile, h) + h = newRstNode(rnIndex) + addSon(h, nil) # no argument + addSon(h, nil) # no options + d.theIndex = newRstNode(rnDefList) + addSon(h, d.theIndex) + addSon(d.indexFile, h) + +proc newDocumentor(filename: string): PDoc = + var s: string + new(result) + result.tocPart = @ [] + result.filename = filename + result.id = 100 + result.splitAfter = 20 + s = getConfigVar("split.item.toc") + if s != "": result.splitAfter = parseInt(s) + +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 ropeFormatNamedVars(frmt: TFormatStr, varnames: openarray[string], + varvalues: openarray[PRope]): PRope = + var + i, j, L, start, idx, num: int + id: string + i = 0 + L = len(frmt) + result = nil + num = 0 + while i <= L + 0 - 1: + if frmt[i] == '$': + inc(i) # skip '$' + case frmt[i] + of '#': + app(result, varvalues[num]) + inc(num) + inc(i) + of '$': + app(result, "$") + inc(i) + of '0'..'9': + j = 0 + while true: + j = (j * 10) + Ord(frmt[i]) - ord('0') + inc(i) + if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break + if j > high(varvalues) + 1: internalError("ropeFormatNamedVars") + num = j + app(result, varvalues[j - 1]) + of 'A'..'Z', 'a'..'z', '\x80'..'\xFF': + id = "" + while true: + add(id, frmt[i]) + inc(i) + if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break + idx = getVarIdx(varnames, id) + if idx >= 0: app(result, varvalues[idx]) + else: rawMessage(errUnkownSubstitionVar, id) + of '{': + id = "" + inc(i) + while frmt[i] != '}': + if frmt[i] == '\0': rawMessage(errTokenExpected, "}") + add(id, frmt[i]) + inc(i) + inc(i) # skip } + # search for the variable: + idx = getVarIdx(varnames, id) + if idx >= 0: app(result, varvalues[idx]) + else: rawMessage(errUnkownSubstitionVar, id) + else: InternalError("ropeFormatNamedVars") + start = i + while (i <= L + 0 - 1): + if (frmt[i] != '$'): inc(i) + else: break + if i - 1 >= start: app(result, copy(frmt, start, i - 1)) + +proc addXmlChar(dest: var string, c: Char) = + case c + of '&': add(dest, "&") + of '<': add(dest, "<") + of '>': add(dest, ">") + of '\"': add(dest, """) + else: add(dest, c) + +proc addRtfChar(dest: var string, c: Char) = + case c + of '{': add(dest, "\\{") + of '}': add(dest, "\\}") + of '\\': add(dest, "\\\\") + else: add(dest, c) + +proc addTexChar(dest: var string, c: Char) = + case c + of '_': add(dest, "\\_") + of '{': add(dest, "\\symbol{123}") + of '}': add(dest, "\\symbol{125}") + of '[': add(dest, "\\symbol{91}") + of ']': add(dest, "\\symbol{93}") + of '\\': add(dest, "\\symbol{92}") + of '$': add(dest, "\\$") + of '&': add(dest, "\\&") + of '#': add(dest, "\\#") + of '%': add(dest, "\\%") + of '~': add(dest, "\\symbol{126}") + of '@': add(dest, "\\symbol{64}") + of '^': add(dest, "\\symbol{94}") + of '`': add(dest, "\\symbol{96}") + else: add(dest, c) + +proc escChar(dest: var string, c: Char) = + if gCmd != cmdRst2Tex: addXmlChar(dest, c) + else: addTexChar(dest, c) + +proc nextSplitPoint(s: string, start: int): int = + result = start + while result < len(s) + 0: + case s[result] + of '_': + return + of 'a'..'z': + if result + 1 < len(s) + 0: + if s[result + 1] in {'A'..'Z'}: return + else: + nil + inc(result) + dec(result) # last valid index + +proc esc(s: string, splitAfter: int = - 1): string = + var j, k, partLen: int + result = "" + if splitAfter >= 0: + partLen = 0 + j = 0 + while j < len(s) + 0: + k = nextSplitPoint(s, j) + if (splitter != " ") or (partLen + k - j + 1 > splitAfter): + partLen = 0 + add(result, splitter) + for i in countup(j, k): escChar(result, s[i]) + inc(partLen, k - j + 1) + j = k + 1 + else: + for i in countup(0, len(s) + 0 - 1): escChar(result, s[i]) + +proc disp(xml, tex: string): string = + if gCmd != cmdRst2Tex: result = xml + else: result = tex + +proc dispF(xml, tex: string, args: openarray[PRope]): PRope = + if gCmd != cmdRst2Tex: result = ropef(xml, args) + else: result = ropef(tex, args) + +proc dispA(dest: var PRope, xml, tex: string, args: openarray[PRope]) = + if gCmd != cmdRst2Tex: appf(dest, xml, args) + else: appf(dest, tex, args) + +proc renderRstToOut(d: PDoc, n: PRstNode): PRope +proc renderAux(d: PDoc, n: PRstNode, outer: string = "$1"): PRope = + result = nil + for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i])) + result = ropef(outer, [result]) + +proc setIndexForSourceTerm(d: PDoc, name: PRstNode, id: int) = + var a, h: PRstNode + if d.theIndex == nil: return + h = newRstNode(rnHyperlink) + a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $(id)) + addSon(h, a) + addSon(h, a) + a = newRstNode(rnIdx) + addSon(a, name) + setIndexPair(d.theIndex, a, h) + +proc renderIndexTerm(d: PDoc, n: PRstNode): PRope = + var a, h: PRstNode + inc(d.id) + result = dispF("<em id=\"$1\">$2</em>", "$2\\label{$1}", + [toRope(d.id), renderAux(d, n)]) + h = newRstNode(rnHyperlink) + a = newRstNode(rnLeaf, d.indexValFilename & disp("#", "") & $(d.id)) + addSon(h, a) + addSon(h, a) + setIndexPair(d.theIndex, n, h) + +proc genComment(d: PDoc, n: PNode): PRope = + var dummyHasToc: bool + if (n.comment != nil) and startsWith(n.comment, "##"): + result = renderRstToOut(d, rstParse(n.comment, true, toFilename(n.info), + toLineNumber(n.info), toColumn(n.info), + dummyHasToc)) + else: + result = nil + +proc genRecComment(d: PDoc, n: PNode): PRope = + if n == nil: + return nil + result = genComment(d, n) + if result == nil: + if not (n.kind in {nkEmpty..nkNilLit}): + for i in countup(0, sonsLen(n) - 1): + result = genRecComment(d, n.sons[i]) + if result != nil: return + else: + n.comment = nil + +proc isVisible(n: PNode): bool = + var v: PIdent + result = false + if n.kind == nkPostfix: + if (sonsLen(n) == 2) and (n.sons[0].kind == nkIdent): + v = n.sons[0].ident + result = (v.id == ord(wStar)) or (v.id == ord(wMinus)) + elif n.kind == nkSym: + result = sfInInterface in n.sym.flags + elif n.kind == nkPragmaExpr: + result = isVisible(n.sons[0]) + +proc getName(n: PNode, splitAfter: int = - 1): string = + case n.kind + of nkPostfix: result = getName(n.sons[1], splitAfter) + of nkPragmaExpr: result = getName(n.sons[0], splitAfter) + of nkSym: result = esc(n.sym.name.s, splitAfter) + of nkIdent: result = esc(n.ident.s, splitAfter) + of nkAccQuoted: result = esc("`") & getName(n.sons[0], splitAfter) & esc("`") + else: + internalError(n.info, "getName()") + result = "" + +proc getRstName(n: PNode): PRstNode = + case n.kind + of nkPostfix: result = getRstName(n.sons[1]) + of nkPragmaExpr: result = getRstName(n.sons[0]) + of nkSym: result = newRstNode(rnLeaf, n.sym.name.s) + of nkIdent: result = newRstNode(rnLeaf, n.ident.s) + of nkAccQuoted: result = getRstName(n.sons[0]) + else: + internalError(n.info, "getRstName()") + result = nil + +proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = + var + r: TSrcGen + kind: TTokType + literal: string + name, result, comm: PRope + if not isVisible(nameNode): return + name = toRope(getName(nameNode)) + result = nil + literal = "" + kind = tkEof + comm = genRecComment(d, n) # call this here for the side-effect! + initTokRender(r, n, {renderNoPragmas, renderNoBody, renderNoComments, + renderDocComments}) + while true: + getNextTok(r, kind, literal) + case kind + of tkEof: + break + of tkComment: + dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}", + [toRope(esc(literal))]) + of tokKeywordLow..tokKeywordHigh: + dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}", + [toRope(literal)]) + of tkOpr, tkHat: + dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}", + [toRope(esc(literal))]) + of tkStrLit..tkTripleStrLit: + dispA(result, "<span class=\"StringLit\">$1</span>", + "\\spanStringLit{$1}", [toRope(esc(literal))]) + of tkCharLit: + dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}", + [toRope(esc(literal))]) + of tkIntLit..tkInt64Lit: + dispA(result, "<span class=\"DecNumber\">$1</span>", + "\\spanDecNumber{$1}", [toRope(esc(literal))]) + of tkFloatLit..tkFloat64Lit: + dispA(result, "<span class=\"FloatNumber\">$1</span>", + "\\spanFloatNumber{$1}", [toRope(esc(literal))]) + of tkSymbol: + dispA(result, "<span class=\"Identifier\">$1</span>", + "\\spanIdentifier{$1}", [toRope(esc(literal))]) + of tkInd, tkSad, tkDed, tkSpaces: + app(result, literal) + of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi, + tkBracketDotLe, tkBracketDotRi, tkCurlyDotLe, tkCurlyDotRi, tkParDotLe, + tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, + tkAccent: + dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}", + [toRope(esc(literal))]) + else: InternalError(n.info, "docgen.genThing(" & toktypeToStr[kind] & ')') + inc(d.id) + app(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"), + ["name", "header", "desc", "itemID"], + [name, result, comm, toRope(d.id)])) + app(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"), + ["name", "header", "desc", "itemID"], [ + toRope(getName(nameNode, d.splitAfter)), result, comm, toRope(d.id)])) + setIndexForSourceTerm(d, getRstName(nameNode), d.id) + +proc renderHeadline(d: PDoc, n: PRstNode): PRope = + var + length: int + refname: PRope + result = nil + for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToOut(d, n.sons[i])) + refname = toRope(rstnodeToRefname(n)) + if d.hasToc: + length = len(d.tocPart) + setlen(d.tocPart, length + 1) + d.tocPart[length].refname = refname + d.tocPart[length].n = n + d.tocPart[length].header = result + result = dispF("<h$1><a class=\"toc-backref\" id=\"$2\" href=\"#$2_toc\">$3</a></h$1>", + "\\rsth$4{$3}\\label{$2}$n", [toRope(n.level), + d.tocPart[length].refname, result, + toRope(chr(n.level - 1 + ord('A')) & "")]) + else: + result = dispF("<h$1 id=\"$2\">$3</h$1>", "\\rsth$4{$3}\\label{$2}$n", [ + toRope(n.level), refname, result, + toRope(chr(n.level - 1 + ord('A')) & "")]) + +proc renderOverline(d: PDoc, n: PRstNode): PRope = + var t: PRope + t = nil + for i in countup(0, rsonsLen(n) - 1): app(t, renderRstToOut(d, n.sons[i])) + result = nil + if d.meta[metaTitle] == nil: + d.meta[metaTitle] = t + elif d.meta[metaSubtitle] == nil: + d.meta[metaSubtitle] = t + else: + result = dispF("<h$1 id=\"$2\"><center>$3</center></h$1>", + "\\rstov$4{$3}\\label{$2}$n", [toRope(n.level), + toRope(rstnodeToRefname(n)), t, toRope(chr(n.level - 1 + ord('A')) & "")]) + +proc renderRstToRst(d: PDoc, n: PRstNode): PRope +proc renderRstSons(d: PDoc, n: PRstNode): PRope = + result = nil + for i in countup(0, rsonsLen(n) - 1): app(result, renderRstToRst(d, n.sons[i])) + +proc renderRstToRst(d: PDoc, n: PRstNode): PRope = + # this is needed for the index generation; it may also be useful for + # debugging, but most code is already debugged... + const + lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+'] + var + L: int + ind: PRope + result = nil + if n == nil: return + ind = toRope(repeatChar(d.indent)) + case n.kind + of rnInner: + result = renderRstSons(d, n) + of rnHeadline: + result = renderRstSons(d, n) + L = ropeLen(result) + result = ropef("$n$1$2$n$1$3", + [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))]) + of rnOverline: + result = renderRstSons(d, n) + L = ropeLen(result) + result = ropef("$n$1$3$n$1$2$n$1$3", + [ind, result, toRope(repeatChar(L, lvlToChar[n.level]))]) + of rnTransition: + result = ropef("$n$n$1$2$n$n", [ind, toRope(repeatChar(78 - d.indent, '-'))]) + of rnParagraph: + result = renderRstSons(d, n) + result = ropef("$n$n$1$2", [ind, result]) + of rnBulletItem: + inc(d.indent, 2) + result = renderRstSons(d, n) + if result != nil: result = ropef("$n$1* $2", [ind, result]) + dec(d.indent, 2) + of rnEnumItem: + inc(d.indent, 4) + result = renderRstSons(d, n) + if result != nil: result = ropef("$n$1(#) $2", [ind, result]) + dec(d.indent, 4) + of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, + rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: + result = renderRstSons(d, n) + of rnDefName: + result = renderRstSons(d, n) + result = ropef("$n$n$1$2", [ind, result]) + of rnDefBody: + inc(d.indent, 2) + result = renderRstSons(d, n) + if n.sons[0].kind != rnBulletList: result = ropef("$n$1 $2", [ind, result]) + dec(d.indent, 2) + of rnField: + result = renderRstToRst(d, n.sons[0]) + L = max(ropeLen(result) + 3, 30) + inc(d.indent, L) + result = ropef("$n$1:$2:$3$4", [ind, result, toRope( + repeatChar(L - ropeLen(result) - 2)), renderRstToRst(d, n.sons[1])]) + dec(d.indent, L) + of rnLineBlockItem: + result = renderRstSons(d, n) + result = ropef("$n$1| $2", [ind, result]) + of rnBlockQuote: + inc(d.indent, 2) + result = renderRstSons(d, n) + dec(d.indent, 2) + of rnRef: + result = renderRstSons(d, n) + result = ropef("`$1`_", [result]) + of rnHyperlink: + result = ropef("`$1 <$2>`_", + [renderRstToRst(d, n.sons[0]), renderRstToRst(d, n.sons[1])]) + of rnGeneralRole: + result = renderRstToRst(d, n.sons[0]) + result = ropef("`$1`:$2:", [result, renderRstToRst(d, n.sons[1])]) + of rnSub: + result = renderRstSons(d, n) + result = ropef("`$1`:sub:", [result]) + of rnSup: + result = renderRstSons(d, n) + result = ropef("`$1`:sup:", [result]) + of rnIdx: + result = renderRstSons(d, n) + result = ropef("`$1`:idx:", [result]) + of rnEmphasis: + result = renderRstSons(d, n) + result = ropef("*$1*", [result]) + of rnStrongEmphasis: + result = renderRstSons(d, n) + result = ropef("**$1**", [result]) + of rnInterpretedText: + result = renderRstSons(d, n) + result = ropef("`$1`", [result]) + of rnInlineLiteral: + inc(d.verbatim) + result = renderRstSons(d, n) + result = ropef("``$1``", [result]) + dec(d.verbatim) + of rnLeaf: + if (d.verbatim == 0) and (n.text == "\\"): + result = toRope("\\\\") # XXX: escape more special characters! + else: + result = toRope(n.text) + of rnIndex: + inc(d.indent, 3) + if n.sons[2] != nil: result = renderRstSons(d, n.sons[2]) + dec(d.indent, 3) + result = ropef("$n$n$1.. index::$n$2", [ind, result]) + of rnContents: + result = ropef("$n$n$1.. contents::", [ind]) + else: rawMessage(errCannotRenderX, rstnodeKindToStr[n.kind]) + +proc renderTocEntry(d: PDoc, e: TTocEntry): PRope = + result = dispF("<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>$n", + "\\item\\label{$1_toc} $2\\ref{$1}$n", [e.refname, e.header]) + +proc renderTocEntries(d: PDoc, j: var int, lvl: int): PRope = + var a: int + result = nil + while (j <= high(d.tocPart)): + a = abs(d.tocPart[j].n.level) + if (a == lvl): + app(result, renderTocEntry(d, d.tocPart[j])) + inc(j) + elif (a > lvl): + app(result, renderTocEntries(d, j, a)) + else: + break + if lvl > 1: + result = dispF("<ul class=\"simple\">$1</ul>", + "\\begin{enumerate}$1\\end{enumerate}", [result]) + +proc fieldAux(s: string): PRope = + result = toRope(strip(s)) + +proc renderImage(d: PDoc, n: PRstNode): PRope = + var + s, scale: string + options: PRope + options = nil + s = getFieldValue(n, "scale") + if s != "": dispA(options, " scale=\"$1\"", " scale=$1", [fieldAux(scale)]) + s = getFieldValue(n, "height") + if s != "": dispA(options, " height=\"$1\"", " height=$1", [fieldAux(s)]) + s = getFieldValue(n, "width") + if s != "": dispA(options, " width=\"$1\"", " width=$1", [fieldAux(s)]) + s = getFieldValue(n, "alt") + if s != "": dispA(options, " alt=\"$1\"", "", [fieldAux(s)]) + s = getFieldValue(n, "align") + if s != "": dispA(options, " align=\"$1\"", "", [fieldAux(s)]) + if options != nil: options = dispF("$1", "[$1]", [options]) + result = dispF("<img src=\"$1\"$2 />", "\\includegraphics$2{$1}", + [toRope(getArgument(n)), options]) + if rsonsLen(n) >= 3: app(result, renderRstToOut(d, n.sons[2])) + +proc renderCodeBlock(d: PDoc, n: PRstNode): PRope = + var + m: PRstNode + g: TGeneralTokenizer + langstr: string + lang: TSourceLanguage + result = nil + if n.sons[2] == nil: return + m = n.sons[2].sons[0] + if (m.kind != rnLeaf): InternalError("renderCodeBlock") + langstr = strip(getArgument(n)) + if langstr == "": + lang = langNimrod # default language + else: + lang = getSourceLanguage(langstr) + if lang == langNone: + rawMessage(warnLanguageXNotSupported, langstr) + result = toRope(m.text) + else: + initGeneralTokenizer(g, m.text) + while true: + getNextToken(g, lang) + case g.kind + of gtEof: + break + of gtNone, gtWhitespace: + app(result, copy(m.text, g.start + 0, g.length + g.start - 1 + 0)) + else: + dispA(result, "<span class=\"$2\">$1</span>", "\\span$2{$1}", [ + toRope(esc(copy(m.text, g.start + 0, g.length + g.start - 1 + 0))), + toRope(tokenClassToStr[g.kind])]) + deinitGeneralTokenizer(g) + if result != nil: + result = dispF("<pre>$1</pre>", "\\begin{rstpre}$n$1$n\\end{rstpre}$n", + [result]) + +proc renderContainer(d: PDoc, n: PRstNode): PRope = + var arg: PRope + result = renderRstToOut(d, n.sons[2]) + arg = toRope(strip(getArgument(n))) + if arg == nil: result = dispF("<div>$1</div>", "$1", [result]) + else: result = dispF("<div class=\"$1\">$2</div>", "$2", [arg, result]) + +proc texColumns(n: PRstNode): string = + result = "" + for i in countup(1, rsonsLen(n)): add(result, "|X") + +proc renderField(d: PDoc, n: PRstNode): PRope = + var + fieldname: string + fieldval: PRope + b: bool + b = false + if gCmd == cmdRst2Tex: + fieldname = addNodes(n.sons[0]) + fieldval = toRope(esc(strip(addNodes(n.sons[1])))) + if cmpIgnoreStyle(fieldname, "author") == 0: + if d.meta[metaAuthor] == nil: + d.meta[metaAuthor] = fieldval + b = true + elif cmpIgnoreStyle(fieldName, "version") == 0: + if d.meta[metaVersion] == nil: + d.meta[metaVersion] = fieldval + b = true + if b: result = nil + else: result = renderAux(d, n, disp("<tr>$1</tr>$n", "$1")) + +proc renderRstToOut(d: PDoc, n: PRstNode): PRope = + if n == nil: + return nil + case n.kind + of rnInner: + result = renderAux(d, n) + of rnHeadline: + result = renderHeadline(d, n) + of rnOverline: + result = renderOverline(d, n) + of rnTransition: + result = renderAux(d, n, disp("<hr />" & "\n", "\\hrule" & "\n")) + of rnParagraph: + result = renderAux(d, n, disp("<p>$1</p>" & "\n", "$1$n$n")) + of rnBulletList: + result = renderAux(d, n, disp("<ul class=\"simple\">$1</ul>" & "\n", + "\\begin{itemize}$1\\end{itemize}" & "\n")) + of rnBulletItem, rnEnumItem: + result = renderAux(d, n, disp("<li>$1</li>" & "\n", "\\item $1" & "\n")) + of rnEnumList: + result = renderAux(d, n, disp("<ol class=\"simple\">$1</ol>" & "\n", + "\\begin{enumerate}$1\\end{enumerate}" & "\n")) + of rnDefList: + result = renderAux(d, n, disp("<dl class=\"docutils\">$1</dl>" & "\n", "\\begin{description}$1\\end{description}" & + "\n")) + of rnDefItem: + result = renderAux(d, n) + of rnDefName: + result = renderAux(d, n, disp("<dt>$1</dt>" & "\n", "\\item[$1] ")) + of rnDefBody: + result = renderAux(d, n, disp("<dd>$1</dd>" & "\n", "$1" & "\n")) + of rnFieldList: + result = nil + for i in countup(0, rsonsLen(n) - 1): + app(result, renderRstToOut(d, n.sons[i])) + if result != nil: + result = dispf("<table 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", + [result]) + of rnField: + result = renderField(d, n) + of rnFieldName: + result = renderAux(d, n, disp("<th class=\"docinfo-name\">$1:</th>", + "\\item[$1:]")) + of rnFieldBody: + result = renderAux(d, n, disp("<td>$1</td>", " $1$n")) + of rnIndex: + result = renderRstToOut(d, n.sons[2]) + of rnOptionList: + result = renderAux(d, n, disp("<table frame=\"void\">$1</table>", "\\begin{description}$n$1\\end{description}" & + "\n")) + of rnOptionListItem: + result = renderAux(d, n, disp("<tr>$1</tr>$n", "$1")) + of rnOptionGroup: + result = renderAux(d, n, disp("<th align=\"left\">$1</th>", "\\item[$1]")) + of rnDescription: + result = renderAux(d, n, disp("<td align=\"left\">$1</td>$n", " $1$n")) + of rnOption, rnOptionString, rnOptionArgument: + InternalError("renderRstToOut") + of rnLiteralBlock: + result = renderAux(d, n, disp("<pre>$1</pre>$n", + "\\begin{rstpre}$n$1$n\\end{rstpre}$n")) + of rnQuotedLiteralBlock: + InternalError("renderRstToOut") + of rnLineBlock: + result = renderAux(d, n, disp("<p>$1</p>", "$1$n$n")) + of rnLineBlockItem: + result = renderAux(d, n, disp("$1<br />", "$1\\\\$n")) + of rnBlockQuote: + result = renderAux(d, n, disp("<blockquote><p>$1</p></blockquote>$n", + "\\begin{quote}$1\\end{quote}$n")) + of rnTable, rnGridTable: + result = renderAux(d, n, disp("<table border=\"1\" class=\"docutils\">$1</table>", "\\begin{table}\\begin{rsttab}{" & + texColumns(n) & "|}$n\\hline$n$1\\end{rsttab}\\end{table}")) + of rnTableRow: + if rsonsLen(n) >= 1: + result = renderRstToOut(d, n.sons[0]) + for i in countup(1, rsonsLen(n) - 1): + dispa(result, "$1", " & $1", [renderRstToOut(d, n.sons[i])]) + result = dispf("<tr>$1</tr>$n", "$1\\\\$n\\hline$n", [result]) + else: + result = nil + of rnTableDataCell: + result = renderAux(d, n, disp("<td>$1</td>", "$1")) + of rnTableHeaderCell: + result = renderAux(d, n, disp("<th>$1</th>", "\\textbf{$1}")) + of rnLabel: + InternalError("renderRstToOut") # used for footnotes and other + of rnFootnote: + InternalError("renderRstToOut") # a footnote + of rnCitation: + InternalError("renderRstToOut") # similar to footnote + of rnRef: + result = dispF("<a class=\"reference external\" href=\"#$2\">$1</a>", + "$1\\ref{$2}", [renderAux(d, n), toRope(rstnodeToRefname(n))]) + of rnStandaloneHyperlink: + result = renderAux(d, n, disp("<a class=\"reference external\" href=\"$1\">$1</a>", + "\\href{$1}{$1}")) + of rnHyperlink: + result = dispF("<a class=\"reference external\" href=\"$2\">$1</a>", + "\\href{$2}{$1}", + [renderRstToOut(d, n.sons[0]), renderRstToOut(d, n.sons[1])]) + of rnDirArg, rnRaw: + result = renderAux(d, n) + of rnImage, rnFigure: + result = renderImage(d, n) + of rnCodeBlock: + result = renderCodeBlock(d, n) + of rnContainer: + result = renderContainer(d, n) + of rnSubstitutionReferences, rnSubstitutionDef: + result = renderAux(d, n, disp("|$1|", "|$1|")) + of rnDirective: + result = renderAux(d, n, "") # Inline markup: + of rnGeneralRole: + result = dispF("<span class=\"$2\">$1</span>", "\\span$2{$1}", + [renderRstToOut(d, n.sons[0]), renderRstToOut(d, n.sons[1])]) + of rnSub: + result = renderAux(d, n, disp("<sub>$1</sub>", "\\rstsub{$1}")) + of rnSup: + result = renderAux(d, n, disp("<sup>$1</sup>", "\\rstsup{$1}")) + of rnEmphasis: + result = renderAux(d, n, disp("<em>$1</em>", "\\emph{$1}")) + of rnStrongEmphasis: + result = renderAux(d, n, disp("<strong>$1</strong>", "\\textbf{$1}")) + of rnInterpretedText: + result = renderAux(d, n, disp("<cite>$1</cite>", "\\emph{$1}")) + of rnIdx: + if d.theIndex == nil: + result = renderAux(d, n, disp("<em>$1</em>", "\\emph{$1}")) + else: + result = renderIndexTerm(d, n) + of rnInlineLiteral: + result = renderAux(d, n, disp("<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>", + "\\texttt{$1}")) + of rnLeaf: + result = toRope(esc(n.text)) + of rnContents: + d.hasToc = true + of rnTitle: + d.meta[metaTitle] = renderRstToOut(d, n.sons[0]) + else: InternalError("renderRstToOut") + +proc generateDoc(d: PDoc, n: PNode) = + if n == nil: return + case n.kind + of nkCommentStmt: + app(d.modDesc, genComment(d, n)) + of nkProcDef: + genItem(d, n, n.sons[namePos], skProc) + of nkMethodDef: + genItem(d, n, n.sons[namePos], skMethod) + of nkIteratorDef: + genItem(d, n, n.sons[namePos], skIterator) + of nkMacroDef: + genItem(d, n, n.sons[namePos], skMacro) + of nkTemplateDef: + genItem(d, n, n.sons[namePos], skTemplate) + of nkConverterDef: + genItem(d, n, n.sons[namePos], skConverter) + of nkVarSection: + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind != nkCommentStmt: + genItem(d, n.sons[i], n.sons[i].sons[0], skVar) + of nkConstSection: + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind != nkCommentStmt: + genItem(d, n.sons[i], n.sons[i].sons[0], skConst) + of nkTypeSection: + for i in countup(0, sonsLen(n) - 1): + if n.sons[i].kind != nkCommentStmt: + genItem(d, n.sons[i], n.sons[i].sons[0], skType) + of nkStmtList: + for i in countup(0, sonsLen(n) - 1): generateDoc(d, n.sons[i]) + of nkWhenStmt: + # generate documentation for the first branch only: + generateDoc(d, lastSon(n.sons[0])) + else: + nil + +proc genSection(d: PDoc, kind: TSymKind) = + if d.section[kind] == nil: return + var title = toRope(copy($kind, 0 + 2) & 's') + d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content"], [ + toRope(ord(kind)), title, toRope(ord(kind) + 50), d.section[kind]]) + d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content"], [ + toRope(ord(kind)), title, toRope(ord(kind) + 50), d.toc[kind]]) + +proc genOutFile(d: PDoc): PRope = + var + code, toc, title, content: PRope + bodyname: string + j: int + j = 0 + toc = renderTocEntries(d, j, 1) + code = nil + content = nil + title = nil + for i in countup(low(TSymKind), high(TSymKind)): + genSection(d, i) + app(toc, d.toc[i]) + if toc != nil: + toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc]) + for i in countup(low(TSymKind), high(TSymKind)): app(code, d.section[i]) + if d.meta[metaTitle] != nil: title = d.meta[metaTitle] + else: title = toRope("Module " & + extractFilename(changeFileExt(d.filename, ""))) + if d.hasToc: bodyname = "doc.body_toc" + else: bodyname = "doc.body_no_toc" + content = ropeFormatNamedVars(getConfigVar(bodyname), ["title", + "tableofcontents", "moduledesc", "date", "time", "content"], [title, toc, + d.modDesc, toRope(getDateStr()), toRope(getClockStr()), code]) + if not (optCompileOnly in gGlobalOptions): + code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title", + "tableofcontents", "moduledesc", "date", "time", "content", "author", + "version"], [title, toc, d.modDesc, toRope(getDateStr()), + toRope(getClockStr()), content, d.meta[metaAuthor], + d.meta[metaVersion]]) + else: + code = content + result = code + +proc generateIndex(d: PDoc) = + if d.theIndex != nil: + sortIndex(d.theIndex) + writeRope(renderRstToRst(d, d.indexFile), gIndexFile) + +proc CommandDoc(filename: string) = + var + ast: PNode + d: PDoc + ast = parseFile(addFileExt(filename, nimExt)) + if ast == nil: return + d = newDocumentor(filename) + initIndexFile(d) + d.hasToc = true + generateDoc(d, ast) + writeRope(genOutFile(d), getOutFile(filename, HtmlExt)) + generateIndex(d) + +proc CommandRstAux(filename, outExt: string) = + var + filen: string + d: PDoc + rst: PRstNode + code: PRope + filen = addFileExt(filename, "txt") + d = newDocumentor(filen) + initIndexFile(d) + rst = rstParse(readFile(filen), false, filen, 0, 1, d.hasToc) + d.modDesc = renderRstToOut(d, rst) + code = genOutFile(d) + writeRope(code, getOutFile(filename, outExt)) + generateIndex(d) + +proc CommandRst2Html(filename: string) = + CommandRstAux(filename, HtmlExt) + +proc CommandRst2TeX(filename: string) = + splitter = "\\-" + CommandRstAux(filename, TexExt) |