index generation for docgen knows about subdirectories; index knows about enum values; fixes import statement for runnableExamples

This commit is contained in:
Araq
2018-09-13 01:05:51 +02:00
parent f14f554435
commit b9ed684dd2
6 changed files with 1337 additions and 41 deletions

View File

@@ -85,8 +85,8 @@ proc init(p: var CodeBlockParams) =
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
config: StringTableRef, filename: string,
options: RstParseOptions,
findFile: FindFileHandler=nil,
msgHandler: MsgHandler=nil) =
findFile: FindFileHandler = nil,
msgHandler: MsgHandler = nil) =
## Initializes a ``RstGenerator``.
##
## You need to call this before using a ``RstGenerator`` with any other
@@ -255,9 +255,9 @@ proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
## .. code-block:: nim
##
## # ...configure gen and rst vars...
## var generatedHTML = ""
## renderRstToOut(gen, rst, generatedHTML)
## echo generatedHTML
## var generatedHtml = ""
## renderRstToOut(gen, rst, generatedHtml)
## echo generatedHtml
proc renderAux(d: PDoc, n: PRstNode, result: var string) =
for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
@@ -282,20 +282,26 @@ proc quoteIndexColumn(text: string): string =
## * ``"\\"`` => ``"\\\\"``
## * ``"\n"`` => ``"\\n"``
## * ``"\t"`` => ``"\\t"``
result = text.replace("\\", "\\\\").replace("\n", "\\n").replace("\t", "\\t")
result = newStringOfCap(text.len + 3)
for c in text:
case c
of '\\': result.add "\\"
of '\L': result.add "\\n"
of '\C': discard
of '\t': result.add "\\t"
else: result.add c
proc unquoteIndexColumn(text: string): string =
## Returns the unquoted version generated by ``quoteIndexColumn``.
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
proc setIndexTerm*(d: var RstGenerator, id, term: string,
proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string,
linkTitle, linkDesc = "") =
## Adds a `term` to the index using the specified hyperlink identifier.
##
## A new entry will be added to the index using the format
## ``term<tab>file#id``. The file part will come from the `filename`
## parameter used in a previous call to the `initRstGenerator()
## <#initRstGenerator>`_ proc.
## ``term<tab>file#id``. The file part will come from the `htmlFile`
## parameter.
##
## The `id` will be appended with a hash character only if its length is not
## zero, otherwise no specific anchor will be generated. In general you
@@ -316,7 +322,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
entry = term
isTitle = false
entry.add('\t')
let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt)
entry.add(htmlFile)
if id.len > 0:
entry.add('#')
@@ -356,7 +361,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
var term = ""
renderAux(d, n, term)
setIndexTerm(d, id, term, d.currentSection)
setIndexTerm(d, "", id, term, d.currentSection)
dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
[id, term])
@@ -364,15 +369,15 @@ type
IndexEntry = 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
linkTitle: string ## contains a prettier text for the href
linkDesc: string ## the title attribute of the final href
IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
## Contains the index sequences for doc types.
##
## The key is a *fake* IndexEntry which will contain the title of the
## document in the `keyword` field and `link` will contain the html
## filename for the document. `linkTitle` and `linkDesc` will be nil.
## filename for the document. `linkTitle` and `linkDesc` will be empty.
##
## The value indexed by this IndexEntry is a sequence with the real index
## entries found in the ``.idx`` file.
@@ -433,13 +438,13 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
var i = 0
while i < symbols.len:
let keyword = symbols[i].keyword
let cleaned_keyword = keyword.escapeLink
let cleanedKeyword = keyword.escapeLink
result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><dd><ul class=\"simple\">\n",
[keyword, cleaned_keyword])
[keyword, cleanedKeyword])
var j = i
while j < symbols.len and keyword == symbols[j].keyword:
let
url = symbols[j].link.escapeLink
url = symbols[j].link #.escapeLink
text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url
desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: ""
if desc.len > 0:
@@ -462,12 +467,12 @@ proc isDocumentationTitle(hyperlink: string): bool =
## for a more detailed explanation.
result = hyperlink.find('#') < 0
proc stripTOCLevel(s: string): tuple[level: int, text: string] =
proc stripTocLevel(s: string): tuple[level: int, text: string] =
## Returns the *level* of the toc along with the text without it.
for c in 0 .. <s.len:
for c in 0 ..< s.len:
result.level = c
if s[c] != ' ': break
result.text = s[result.level .. <s.len]
result.text = s[result.level ..< s.len]
proc indentToLevel(level: var int, newLevel: int): string =
## Returns the sequence of <ul>|</ul> characters to switch to `newLevel`.
@@ -483,7 +488,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
result = repeat("</ul></li>", level - newLevel)
level = newLevel
proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
proc generateDocumentationToc(entries: seq[IndexEntry]): string =
## Returns the sequence of index entries in an HTML hierarchical list.
result = ""
# Build a list of levels and extracted titles to make processing easier.
@@ -495,7 +500,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
level = 1
levels.newSeq(entries.len)
for entry in entries:
let (rawLevel, rawText) = stripTOCLevel(entry.linkTitle or entry.keyword)
let (rawLevel, rawText) = stripTocLevel(entry.linkTitle or entry.keyword)
if rawLevel < 1:
# This is a normal symbol, push it *inside* one level from the last one.
levels[L].level = level + 1
@@ -519,9 +524,9 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
titleTag = levels[L].text
else:
result.add(level.indentToLevel(levels[L].level))
result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2">
result.addf("""<li><a class="reference" data-doc-search-tag="$1: $2" href="$3">
$3</a></li>
""", [titleTag & " : " & levels[L].text, link, levels[L].text])
""", [titleTag, levels[L].text, link, levels[L].text])
inc L
result.add(level.indentToLevel(1) & "</ul>\n")
@@ -534,7 +539,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
sort(titles, cmp)
for title in titles:
let tocList = generateDocumentationTOC(docs.getOrDefault(title))
let tocList = generateDocumentationToc(docs.getOrDefault(title))
result.add("<ul><li><a href=\"" &
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</li></ul>\n")
@@ -575,8 +580,8 @@ proc readIndexDir(dir: string):
setLen(result.symbols, 0)
var L = 0
# Scan index files and build the list of symbols.
for kind, path in walkDir(dir):
if kind == pcFile and path.endsWith(IndexExt):
for path in walkDirRec(dir):
if path.endsWith(IndexExt):
var
fileEntries: seq[IndexEntry]
title: IndexEntry
@@ -606,7 +611,7 @@ proc readIndexDir(dir: string):
inc F
# Depending on type add this to the list of symbols or table of APIs.
if title.keyword.len == 0:
for i in 0 .. <F:
for i in 0 ..< F:
# Don't add to symbols TOC entries (they start with a whitespace).
let toc = fileEntries[i].linkTitle
if toc.len > 0 and toc[0] == ' ':
@@ -615,7 +620,12 @@ proc readIndexDir(dir: string):
setLen(result.symbols, L + 1)
result.symbols[L] = fileEntries[i]
inc L
result.modules.add(path.splitFile.name)
if fileEntries.len > 0:
var x = fileEntries[0].link
let i = find(x, '#')
if i > 0:
x = x.substr(0, i-1)
result.modules.add(x.changeFileExt(""))
else:
# Generate the symbolic anchor for index quickjumps.
title.linkTitle = "doc_toc_" & $result.docs.len
@@ -676,7 +686,7 @@ proc mergeIndexes*(dir: string): string =
# ----------------------------------------------------------------------------
proc stripTOCHTML(s: string): string =
proc stripTocHtml(s: string): string =
## Ugly quick hack to remove HTML tags from TOC titles.
##
## A TocEntry.header field already contains rendered HTML tags. Instead of
@@ -724,7 +734,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
# Generate index entry using spaces to indicate TOC level for the output HTML.
assert n.level >= 0
setIndexTerm(d, refname, tmp.stripTOCHTML,
setIndexTerm(d, "", refname, tmp.stripTocHtml,
spaces(max(0, n.level)) & tmp)
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
@@ -872,7 +882,7 @@ 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):
tuple[beginTable, endTable: string] =
## Returns the necessary tags to start/end a code block in HTML.
##
@@ -922,7 +932,7 @@ 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)
let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text)
dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
if params.lang == langNone: